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.
- 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.
- In the API Keys card, select Create API Key.
- Type a Name that says what the key is for, such as "WordPress migration".
- 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.
- Select Submit, then copy the API Key and Secret. The secret is shown only once.
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 toPaused) 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
pageanditemsPerPage(up to 100) to page through it. Thepaginationobject in the response carries the totals. - Changes since. Every list accepts
updated_after, a full timestamp such as2026-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.
PUTandPATCHon a single record change only the fields you send. - Validation. A payload that breaks a rule answers 422 with an
errorsobject 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.
| Resource | Upsert endpoint | Matched on |
|---|---|---|
| Website | PUT /websites/upsert | url |
| Page | PUT /websites/{website}/pages/upsert | slug |
| Section | PUT /websites/{website}/sections/upsert | name |
| Menu | PUT /websites/{website}/menus/upsert | name |
| FAQ category | PUT /websites/{website}/faq-categories/upsert | name |
| FAQ | PUT /websites/{website}/faqs/upsert | slug (by default the slug of the question) |
| Redirect | PUT /websites/{website}/redirects/upsert, or /bulk for up to 500 | from |
| Article category | PUT /article-categories/upsert | slug |
| Article | PUT /articles/upsert | slug |
| Form | PUT /forms/upsert | slug |
| Media | POST /media | source_url, when you send one |
Websites
| Method and path | What it does |
|---|---|
GET /websites | Lists the organisation's websites. Filter with url. |
GET /websites/{website} | One website. |
POST /websites | Creates a website from name and url. |
PUT /websites/upsert | Creates 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.
| type | Send | Shows in the editor as |
|---|---|---|
text | value: a short string | Short Text |
long_text | value: plain text | Long Text |
html | value: an HTML string | Rich Text |
image | media_id: one media library id | Single Image |
gallery | media_ids: up to 50 media library ids, in order | Gallery |
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.
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 path | What it does |
|---|---|
GET /websites/{website}/pages | Lists pages without their blocks. Filter with slug. |
GET /websites/{website}/pages/{page} | One page with its blocks. |
POST /websites/{website}/pages | Creates a page. |
PUT /websites/{website}/pages/upsert | Creates or updates by slug. |
PUT or PATCH /websites/{website}/pages/{page} | Changes the fields you send. |
| Field | Notes |
|---|---|
name | Required when creating. The page's name in the editor. |
slug | Required 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_slug | The slug of the page this one sits under, or null. The parent must already exist, so send parent pages first. |
sort | The page's menu order, a whole number. |
meta_title, meta_description | What search engines show. Up to 255 and 1,000 characters. |
no_index, show_in_menu | True or false. |
contents | The 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 aurlthat 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 slughomepage. - Any other path, such as
/events, is kept as an internal link. - An address starting with
https://,mailto:ortel: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).
| Field | Notes |
|---|---|
slug | Required when creating. Unique within the organisation; the website finds the article by it. |
heading | Required when creating. |
sub_heading, author, abstract | Optional text. |
html | The article body as HTML. |
status | Published (the default for a new article) or Draft. |
published_at | The 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. |
categories | Category slugs. They must exist already. Sending the list replaces the article's categories. |
media_id | A media library id, used as both the banner and the card image. null removes the image. |
meta_title, meta_description, no_index | As 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_urlis a label you choose, such as the image's address on the old site. When an image with the samesource_urlis already in the library, that image is returned and the new upload is not stored again; anameoraltyou send replaces the existing image's. Two uploads with the samesource_urlat 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).
| type | Use it for |
|---|---|
short-text, long-text | A line or a paragraph of text. |
email-input, phone-input, number-text, date-input | An email address, a phone number, a number or a date. |
select-list | One choice from options (a drop-down or radio buttons on the old site). |
multi-select | Several choices from options (checkboxes). |
switch-input | A single yes or no. |
rating, nps | A star rating up to max, or a 0 to 10 score. |
file-upload | Files, 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
- Create the key with the Website Content groups it will write to ticked, as described at the top of this guide.
- Upsert the website by its address and note its
id. - Upload every image with
POST /media, passing its old address assource_url, and rewrite image addresses in your HTML to theurlthat comes back. - Upsert the article categories, then the articles.
- Upsert the forms.
- Upsert the pages, parents before children, then the sections.
- Upsert the menus, after the pages, so their links find the pages.
- Upsert the FAQ categories with their questions.
- Send the redirects with the bulk endpoint, 500 at a time.
- 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.