Gorilla Dash

Help Centre

Guides for every part of Gorilla Dash

Step-by-step how-to guides written by the team that builds the product. Read them here, or download any guide as a PDF.

API

Managing website content through the API

Download PDF

The website content endpoints let a developer read, create and update an organisation's websites, pages, sections, menus, FAQs, redirects, articles, media and forms, for example to move an existing website into Gorilla Dash.

Last updated October 14, 2026


Most of the Gorilla Dash API reads content that someone wrote in Gorilla Dash. The endpoints in this guide work the other way round: they let a program write an organisation's website content, so that a whole website can be loaded from somewhere else, such as an old WordPress site, and kept in step afterwards. Everything they write appears in the same editors your team already uses, and the website reads it through the website API exactly as if someone had typed it in.

Every endpoint lives under https://api.gorilladash.com/api/v1/website-content. This guide assumes you have read API keys: where to find them and what they can see and How API responses are shaped, and what the errors mean.

The key you need

These endpoints accept only a named organisation API key that was given them when it was created. Every other key is refused with 403, including the organisation's original key and access token, tribe keys, and named keys created without them. This is on purpose: keys handed to a point-of-sale or CRM integration should never be able to rewrite your website.

  1. Open Organisation Settings and choose API Connections. You need the Edit Websites permission to give a key endpoints that create or change content, and View Websites or Edit Websites for endpoints that only read it.
  2. In the API Keys card, select Create API Key.
  3. Type a Name that says what the key is for, such as "WordPress migration".
  4. Under Permissions, find the Website Content heading. Tick the box beside each group the tool needs (Websites, Pages, Menus, Website Media, Website Forms and so on) to give it every endpoint in that group, or open a group and tick only the endpoints it calls, such as the GET ones for a tool that only reads. A key can call only what it is given.
  5. Select Submit, then copy the API Key and Secret. The secret is shown only once.
A KEY THAT CAN WRITE CONTENT CAN CHANGE THE WHOLE WEBSITE
A key given the POST and PUT website content endpoints can overwrite the pages, menus, articles, media and forms of every website in the organisation. Give it only to a tool you trust with the whole website, set an expiry date when the job has an end, and revoke the key from the API Keys card as soon as the work is finished. The access cannot be changed on an existing key; create a new key instead.

How every endpoint behaves

  • Status code. Every successful call answers 201, reads included, with the record in data.
  • Nothing is ever deleted. There are no delete endpoints. The API only creates and updates; removing a website, page, section, menu, FAQ, redirect, article, category, image or form is done by a person in Gorilla Dash. The only things a write removes are the entries of a list you replace on a record you are updating: a page's or section's blocks, a menu's items, a category's questions. To take something off the website from your program, change its status where it has one (an article to Draft, a form to Paused) or leave it out of the menus.
  • Your organisation only. A website, page, article or any other record that belongs to another organisation answers 404, exactly as if it did not exist.
  • Organisation-level content only. Pages, sections, menus, FAQs and articles a tribe has its own copy of are left to the editors in Gorilla Dash and are not listed or changed here.
  • Lists. Every list is newest first by id, 25 to a page. Send page and itemsPerPage (up to 100) to page through it. The pagination object in the response carries the totals.
  • Changes since. Every list accepts updated_after, a full timestamp such as 2026-10-06T09:00:00+10:00. Only records whose own row changed after that moment come back. A list leaves out child records (a page's blocks, a menu's items, a category's questions); fetch a single record to read those.
  • Writes return the record. Create, update and upsert all answer with the saved record, so you learn its id without a second call.
  • Updates are partial. PUT and PATCH on a single record change only the fields you send.
  • Validation. A payload that breaks a rule answers 422 with an errors object naming each field. Nothing is written when a call is refused.

Upsert: sending the same thing twice

Each collection has an upsert endpoint that finds a record by its natural key (a page's slug, a menu's name, a redirect's path) and updates it, or creates it when there is none. Sending the same payload again changes nothing: no duplicate is created, no row is rewritten, and the record keeps its id. This is what makes a migration safe to re-run after a failure or after the source site changes. If someone deleted a website, page, menu, form or article in Gorilla Dash, an upsert with the same natural key brings that record back rather than creating a second one.

The plain create endpoint (POST) refuses a natural key that already exists with a 422, so use it only when a duplicate would be a mistake.

ResourceUpsert endpointMatched on
WebsitePUT /websites/upserturl
PagePUT /websites/{website}/pages/upsertslug
SectionPUT /websites/{website}/sections/upsertname
MenuPUT /websites/{website}/menus/upsertname
FAQ categoryPUT /websites/{website}/faq-categories/upsertname
FAQPUT /websites/{website}/faqs/upsertslug (by default the slug of the question)
RedirectPUT /websites/{website}/redirects/upsert, or /bulk for up to 500from
Article categoryPUT /article-categories/upsertslug
ArticlePUT /articles/upsertslug
FormPUT /forms/upsertslug
MediaPOST /mediasource_url, when you send one

Websites

Method and pathWhat it does
GET /websitesLists the organisation's websites. Filter with url.
GET /websites/{website}One website.
POST /websitesCreates a website from name and url.
PUT /websites/upsertCreates or updates by url.
PUT /websites/{website}Changes the name or URL.

A website comes back as id, name, url, default_website, graphql_client_id, created_at and updated_at. A new website gets its website API client straight away; graphql_client_id is that client's id. Its secret is never returned by this API: copy it from the website's settings in Gorilla Dash.

Content blocks

Pages and sections hold their content as an ordered list of blocks. Each block has a name and a type; the same name may appear more than once to make a list, and the website reads blocks in the order you send them.

typeSendShows in the editor as
textvalue: a short stringShort Text
long_textvalue: plain textLong Text
htmlvalue: an HTML stringRich Text
imagemedia_id: one media library idSingle Image
gallerymedia_ids: up to 50 media library ids, in orderGallery

An example: {"name": "Banner Image", "type": "image", "media_id": 812}. Upload the image first (see Media below) and use the id that comes back. An image block holds only the image; if the website needs a link or caption beside it, send a text block for that too, for example Partner followed by Partner Link. A page or section holds at most 200 blocks.

SENDING BLOCKS REPLACES THEM
When a write includes contents, the list replaces the record's blocks: the first block you send updates the first stored block, and so on, blocks beyond the end of your list are deleted, and new ones are added. Leave contents out to change only the other fields. Blocks a page template defines are never touched. When you read a page, each block also has an id, its media with URLs, and an editor_type; a block added in the editor with a type this API does not write comes back as unsupported.

Pages

Method and pathWhat it does
GET /websites/{website}/pagesLists pages without their blocks. Filter with slug.
GET /websites/{website}/pages/{page}One page with its blocks.
POST /websites/{website}/pagesCreates a page.
PUT /websites/{website}/pages/upsertCreates or updates by slug.
PUT or PATCH /websites/{website}/pages/{page}Changes the fields you send.
FieldNotes
nameRequired when creating. The page's name in the editor.
slugRequired when creating. The page's address on the site, without slashes at either end. It may contain slashes inside, such as about/board-of-directors. Unique within the website.
parent_slugThe slug of the page this one sits under, or null. The parent must already exist, so send parent pages first.
sortThe page's menu order, a whole number.
meta_title, meta_descriptionWhat search engines show. Up to 255 and 1,000 characters.
no_index, show_in_menuTrue or false.
contentsThe page's blocks, as described above.

Sections

A section is a named group of blocks that several pages share, such as the partner logos in a footer. It has a name, unique within the website, and contents. The endpoints follow the pages pattern under /websites/{website}/sections, with the list filtered by name and upsert matched on it.

Menus

A menu has a name, unique within the website, and items: a tree of up to four levels and 500 items in all. Each item has a label and, optionally, a url, a page_id and children. The endpoints follow the pages pattern under /websites/{website}/menus.

  • An item with a page_id, or with a url that is a path naming one of the website's pages (/about, /about/ or /index.php/about/), links to that page and follows it if the page is renamed later. / links to the page with the slug homepage.
  • Any other path, such as /events, is kept as an internal link.
  • An address starting with https://, mailto: or tel: is kept as an external link.
  • An item with neither is a heading with no link, which is useful for a drop-down parent.

Sending items replaces the whole tree. The menu is rebuilt only when the tree you send differs from the stored one, and the menu editor's history keeps a copy of the previous version each time it is. When you read a menu, each item has a type of page, internal_url, external_url or placeholder.

FAQ categories and FAQs

An FAQ category has a name, unique within the website, an optional sort, and optionally faqs: up to 200 questions as question, answer (HTML) and an optional slug. Sending faqs replaces the category's questions in the order you give, the same way blocks are replaced. Use /websites/{website}/faq-categories for this.

To work on one question at a time, use /websites/{website}/faqs. A question needs faq_category_id (a category on the same website) and question, and takes answer, slug and sort. Its slug, by default the slug of the question, is unique within the website and is what upsert matches on. The list can be filtered by slug or faq_category_id.

Redirects

A redirect sends visitors from an old path on the site, from, to a new path or a full address, to. from must start with a slash and is unique within the website; to is a path starting with a single slash or an address starting with http:// or https://. Both are up to 255 characters with no spaces. The single-record endpoints follow the pages pattern under /websites/{website}/redirects, with the list filtered by from.

PUT /websites/{website}/redirects/bulk takes {"redirects": [{"from": "/NAB", "to": "https://www.nab.com.au/"}]} with up to 500 redirects, each path once. Each one is created or updated by its from; redirects you do not send are never changed or deleted. It answers with counts rather than records: {"created": 2, "updated": 1, "unchanged": 40}.

A redirect whose target leads straight back to itself is saved with is_loop set to true, and the website leaves it out rather than loop visitors forever. To stop a redirect from your program, point it somewhere else; removing one is done in Gorilla Dash.

Article categories and articles

Article categories live under /article-categories. A category has a slug (unique within the organisation), a name, and an optional heading and caption.

Articles live under /articles and are the organisation's own news and blog posts. The list leaves out the body, categories and image, and can be filtered by slug, status and category (a category slug).

FieldNotes
slugRequired when creating. Unique within the organisation; the website finds the article by it.
headingRequired when creating.
sub_heading, author, abstractOptional text.
htmlThe article body as HTML.
statusPublished (the default for a new article) or Draft.
published_atThe article's date, a full timestamp. The website shows it and lists articles newest first by it, so a migrated article keeps its original date. Defaults to the moment it is created.
categoriesCategory slugs. They must exist already. Sending the list replaces the article's categories.
media_idA media library id, used as both the banner and the card image. null removes the image.
meta_title, meta_description, no_indexAs for pages.

To take an article off the website, set its status to Draft. An article deleted in Gorilla Dash is left out of the list and answers 404, and an upsert with its slug brings it back as Published unless you send another status.

Media

POST /media adds an image to the organisation's media library, the same library the media picker shows. Send it as multipart/form-data with the image in file and, optionally, name, alt and source_url. It answers with id, name, alt, url and source_url. Use the id in blocks and articles, and the url (the original image on the Gorilla Dash CDN) inside HTML.

  • Only JPEG, PNG, GIF and WebP images up to 20 MB are accepted. SVG is refused, because an SVG file can carry script.
  • The API never downloads anything for you. Download the image yourself and upload the file.
  • source_url is a label you choose, such as the image's address on the old site. When an image with the same source_url is already in the library, that image is returned and the new upload is not stored again; a name or alt you send replaces the existing image's. Two uploads with the same source_url at once wait for each other, and one that waits more than a few seconds answers 409: send it again. GET /media?source_url=... tells you whether an image is there before you download it.
  • The cropped sizes the website API offers are made in the background a little after the upload. Until then they come back empty and only the original is available.

GET /media and GET /media/{media} read the library. The older GET /api/v1/media list described in Media library images on the API is unchanged.

Forms

Standalone forms live under /forms. Unlike the older GET /api/v1/forms, which lists only live forms, these endpoints see every form of the organisation whatever its status, and the list can be filtered by slug and status. A form takes the same settings as the form builder: name (required), slug (lowercase letters, digits and dashes, unique within the organisation and part of the form's public address; generated from the name when left out), status (Draft, Active or Paused; only an Active form accepts answers on the website), heading, message, submit_label, thankyou, redirect_url, notify_emails, notify_tribe, create_person and fields. A form read with a key that cannot change forms leaves out notify_emails, because those are your staff's addresses. A key given any endpoint that creates or changes forms (the POST or either PUT endpoint), the whole Website Forms group, or Full Access gets them.

Each field has a key, a label and a type, and optionally required, help, placeholder, options (as {"label": ..., "value": ...}) and maps_to (first_name, last_name, email or phone, to match the answer to a contact).

typeUse it for
short-text, long-textA line or a paragraph of text.
email-input, phone-input, number-text, date-inputAn email address, a phone number, a number or a date.
select-listOne choice from options (a drop-down or radio buttons on the old site).
multi-selectSeveral choices from options (checkboxes).
switch-inputA single yes or no.
rating, npsA star rating up to max, or a 0 to 10 score.
file-uploadFiles, up to max_files.

There is no field for a paragraph of text between fields; put that copy in the form's message or in a field's help. A form deleted in Gorilla Dash keeps its submissions and its slug, and an upsert with that slug brings it back.

Moving a website in, step by step

  1. Create the key with the Website Content groups it will write to ticked, as described at the top of this guide.
  2. Upsert the website by its address and note its id.
  3. Upload every image with POST /media, passing its old address as source_url, and rewrite image addresses in your HTML to the url that comes back.
  4. Upsert the article categories, then the articles.
  5. Upsert the forms.
  6. Upsert the pages, parents before children, then the sections.
  7. Upsert the menus, after the pages, so their links find the pages.
  8. Upsert the FAQ categories with their questions.
  9. Send the redirects with the bulk endpoint, 500 at a time.
  10. Revoke the key when the website is live, or keep a key that can write content only if something will keep it in step.

Because every step is an upsert, the whole run can be repeated at any time. Each call purges the website's cache, so a change shows on the live website within a few minutes.

Next · AppointmentsAppointments and the Event Calendar are different things