Gravi API · v1

Работайте со страницами Gravi из кода

JSON API для всего, что умеет редактор: создавайте страницы, добавляйте и расставляйте блоки, читайте ответы форм и аналитику. Ключи принадлежат проекту на тарифе Pro.

Базовый URL
https://api.gravi.page/v1
Документ OpenAPI 3.1
https://api.gravi.page/v1/openapi.json
На этой странице

Быстрый старт

  1. Создайте ключ в настройках проекта → API. Он показывается один раз — сохраните его в надёжном месте.
  2. Передавайте его как bearer-токен. Каждый ответ — JSON:
    curl "https://api.gravi.page/v1/project" \
      -H "Authorization: Bearer $GRAVI_API_KEY"
  3. Добавьте блок на страницу. Без позиции он займёт первое свободное место, а открытые редакторы обновятся сразу:
    curl -X POST "https://api.gravi.page/v1/pages/$PAGE_ID/blocks" \
      -H "Authorization: Bearer $GRAVI_API_KEY" \
      -H "Content-Type: application/json" \
      -d '{"type":"text","markdown":"Hello from the **API**"}'

Ниже в справочнике — все эндпоинты с параметрами и ответами.

Аутентификация

Передавайте ключ в заголовке Authorization: Authorization: Bearer gravi_…. Ключ действует от имени создавшего его владельца и видит только свой проект. Не храните ключи в браузере или открытом репозитории: API не принимает кросс-доменные запросы.

Ключ перестаёт работать, если его отозвали или срок истёк, если создатель больше не владелец проекта или проект ушёл с тарифа Pro (403 PLAN_REQUIRED; ключ сохраняется).

Права доступа

У каждого ключа есть права (scopes); запрос без нужного права получает 403 FORBIDDEN.

  • content:readЧтение проекта, страниц и блоков.
  • content:writeСоздание и изменение страниц, блоков, шапки, темы, файлов и паролей блоков.
  • responses:readЧтение форм и ответов.
  • responses:writeИзменение и удаление ответов. В ответах персональные данные, поэтому в готовых наборах этого права нет.
  • analytics:readЧтение аналитики страниц.

Лимиты запросов

На ключ: 120 запросов в минуту (всплески до 30), из них 30 — изменяющие данные. Сверх лимита API отвечает 429 с заголовком Retry-After в секундах.

JSON-тело до 256 КБ; изображения — до размера загрузки вашего тарифа.

Ошибки

У всех ошибок один формат. Ориентируйтесь на code, а не на текст сообщения:

{
  "error": {
    "code": "VALIDATION",
    "message": "The request is not valid.",
    "details": { "field": "slug" }
  }
}
СтатусКоды
401UNAUTHENTICATEDACCOUNT_BLOCKEDACCOUNT_DELETED
403FORBIDDENPLAN_REQUIREDLIMIT_REACHED
404NOT_FOUND
409CONFLICTSLUG_TAKENSLUG_RESERVED
422SLUG_INVALIDVALIDATION
429RATE_LIMITED
502FETCH_FAILED

Постраничный вывод

Списки возвращают { data, nextCursor }. Передавайте limit (1–100, по умолчанию 50) и nextCursor предыдущей страницы как cursor, пока он не станет null.

Справочник

Сгенерирован из документа OpenAPI. Описания эндпоинтов и полей — на английском, как и сам API.

GET/project

Get the key's project

Право: Любой ключ

Name, plan and limits, usage, public URLs and the block types with their sizes.

Ответ

200Project

curl "https://api.gravi.page/v1/project" \
  -H "Authorization: Bearer $GRAVI_API_KEY"
GET/pages

List pages

Право: content:read

Параметры запроса

  • limitinteger1–100 · default 50
  • cursorstring≤ 200 chars

Ответ

200PageList

curl "https://api.gravi.page/v1/pages" \
  -H "Authorization: Bearer $GRAVI_API_KEY"
POST/pages

Create a page

Право: content:write

Creates a published page from an optional template.

Тело запроса

  • slugstringобязательно1–200 chars
  • template"blank" | "creator" | "developer" | "business"
  • titlestring≤ 80 chars

Ответ

201Page

curl -X POST "https://api.gravi.page/v1/pages" \
  -H "Authorization: Bearer $GRAVI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"slug":"jane","title":"Jane Doe"}'
GET/pages/{pageId}

Get a page with its blocks

Право: content:read

Параметры пути

  • pageIdstringобязательно

    Page id (from GET /v1/pages).

Ответ

200Page

curl "https://api.gravi.page/v1/pages/{pageId}" \
  -H "Authorization: Bearer $GRAVI_API_KEY"
PATCH/pages/{pageId}

Update a page's address, status, SEO or language

Право: content:write

Параметры пути

  • pageIdstringобязательно

    Page id (from GET /v1/pages).

Тело запроса

  • slugstring1–200 chars
  • status"published" | "unpublished"
  • seoobject
    • titlestring или null≤ 70 chars
    • descriptionstring или null≤ 200 chars
    • noindexboolean
  • locale"en" | "ru"

Ответ

200Page

curl -X PATCH "https://api.gravi.page/v1/pages/{pageId}" \
  -H "Authorization: Bearer $GRAVI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status":"published","seo":{"title":"Jane Doe — designer"}}'
DELETE/pages/{pageId}

Delete a page

Право: content:write

Soft delete: the page can be restored from the dashboard for 30 days.

Параметры пути

  • pageIdstringобязательно

    Page id (from GET /v1/pages).

Ответ

204Без содержимого

curl -X DELETE "https://api.gravi.page/v1/pages/{pageId}" \
  -H "Authorization: Bearer $GRAVI_API_KEY"
PATCH/pages/{pageId}/header

Update the page header

Право: content:write

bio takes Markdown; avatarFileId: null removes the avatar.

Параметры пути

  • pageIdstringобязательно

    Page id (from GET /v1/pages).

Тело запроса

  • titlestring≤ 80 chars
  • biostring≤ 40000 chars
  • bioJsonany
  • avatarFileIdstring или null1–64 chars

Ответ

200Page

curl -X PATCH "https://api.gravi.page/v1/pages/{pageId}/header" \
  -H "Authorization: Bearer $GRAVI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"Jane Doe","bio":"Designer in **Lisbon**. Say hi at [jane.dev](https://jane.dev)."}'
PUT/pages/{pageId}/theme

Set the page theme

Право: content:write

Параметры пути

  • pageIdstringобязательно

    Page id (from GET /v1/pages).

Тело запроса

  • presetstringобязательно1–64 chars
  • overridesmap of any

Ответ

200Page

curl -X PUT "https://api.gravi.page/v1/pages/{pageId}/theme" \
  -H "Authorization: Bearer $GRAVI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"preset":"paper"}'
GET/pages/{pageId}/blocks

List blocks

Право: content:read

In desktop reading order.

Параметры пути

  • pageIdstringобязательно

    Page id (from GET /v1/pages).

Параметры запроса

  • limitinteger1–100 · default 50
  • cursorstring≤ 200 chars

Ответ

200BlockList

curl "https://api.gravi.page/v1/pages/{pageId}/blocks" \
  -H "Authorization: Bearer $GRAVI_API_KEY"
POST/pages/{pageId}/blocks

Add a block

Право: content:write

Shorthands: url (link, video), markdown (text, article), imageUrl / fileId (image). Without position the block takes the first free spot. An existing id returns the stored block with 200 (idempotent retries).

Параметры пути

  • pageIdstringобязательно

    Page id (from GET /v1/pages).

Тело запроса

  • idstring
  • typestringобязательно1–32 chars
  • paramsmap of any
  • urlstring1–2048 chars
  • markdownstring≤ 40000 chars
  • imageUrlstring1–2048 chars
  • fileIdstring1–64 chars
  • size"icon" | "compact" | "small" | "banner" | "wide" | "large" | "auto" | "full" | "full_auto" | object
  • position"end" | "start" | "auto" | object

Ответ

201Block

curl -X POST "https://api.gravi.page/v1/pages/{pageId}/blocks" \
  -H "Authorization: Bearer $GRAVI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"type":"link","url":"https://github.com/jane","size":"banner"}'
PUT/pages/{pageId}/blocks/order

Reorder blocks

Право: content:write

Each list names every block once; blocks re-flow in that order, keeping sizes.

Параметры пути

  • pageIdstringобязательно

    Page id (from GET /v1/pages).

Тело запроса

  • desktoparray of string≤ 1000 items
  • mobilearray of string≤ 1000 items

Ответ

200BlockOrderResult

curl -X PUT "https://api.gravi.page/v1/pages/{pageId}/blocks/order" \
  -H "Authorization: Bearer $GRAVI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"desktop":["intro","github","photo"]}'
PATCH/pages/{pageId}/blocks/{blockId}

Update a block

Право: content:write

params is partial (null removes a key); markdown replaces text content.

Параметры пути

  • pageIdstringобязательно

    Page id (from GET /v1/pages).

  • blockIdstringобязательно

    Block id (id of a block).

Тело запроса

  • paramsmap of any
  • markdownstring≤ 40000 chars
  • size"icon" | "compact" | "small" | "banner" | "wide" | "large" | "auto" | "full" | "full_auto" | object

Ответ

200Block

curl -X PATCH "https://api.gravi.page/v1/pages/{pageId}/blocks/{blockId}" \
  -H "Authorization: Bearer $GRAVI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"markdown":"Hello **world**","size":"wide"}'
DELETE/pages/{pageId}/blocks/{blockId}

Delete a block

Право: content:write

Параметры пути

  • pageIdstringобязательно

    Page id (from GET /v1/pages).

  • blockIdstringобязательно

    Block id (id of a block).

Ответ

204Без содержимого

curl -X DELETE "https://api.gravi.page/v1/pages/{pageId}/blocks/{blockId}" \
  -H "Authorization: Bearer $GRAVI_API_KEY"
PUT/pages/{pageId}/blocks/{blockId}/lock

Protect a block with a password

Право: content:write

A new password (+ hint), or lockId of another locked block on the page to share its password. Visitors see a placeholder until they unlock it; the content never reaches them.

Параметры пути

  • pageIdstringобязательно

    Page id (from GET /v1/pages).

  • blockIdstringобязательно

    Block id (id of a block).

Тело запроса

Один из вариантов:

  • passwordstringобязательно4–128 chars
  • hintstring≤ 80 chars
  • lockIdstringобязательно1–64 chars

Ответ

200Block

curl -X PUT "https://api.gravi.page/v1/pages/{pageId}/blocks/{blockId}/lock" \
  -H "Authorization: Bearer $GRAVI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"password":"open-sesame","hint":"The magic words"}'
DELETE/pages/{pageId}/blocks/{blockId}/lock

Remove a block's password

Право: content:write

Параметры пути

  • pageIdstringобязательно

    Page id (from GET /v1/pages).

  • blockIdstringобязательно

    Block id (id of a block).

Ответ

204Без содержимого

curl -X DELETE "https://api.gravi.page/v1/pages/{pageId}/blocks/{blockId}/lock" \
  -H "Authorization: Bearer $GRAVI_API_KEY"
POST/files

Upload an image

Право: content:write

multipart/form-data with a file field (up to the plan's upload size), or JSON { url } to import an image. Unused files are removed after 24 hours.

Тело запроса

Также принимает multipart/form-data с полем file.

  • urlstringобязательно1–2048 chars

Ответ

201File

curl -X POST "https://api.gravi.page/v1/files" \
  -H "Authorization: Bearer $GRAVI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/photo.jpg"}'
GET/forms

List forms

Право: responses:read

Newest response first. Forms whose block was deleted keep their responses.

Параметры запроса

  • limitinteger1–100 · default 50
  • cursorstring≤ 200 chars

Ответ

200FormList

curl "https://api.gravi.page/v1/forms" \
  -H "Authorization: Bearer $GRAVI_API_KEY"
GET/forms/{formId}/responses

List a form's responses

Право: responses:read

Newest first (with q: by relevance). Without status, spam is left out. from / to bound the submission time (to exclusive).

Параметры пути

  • formIdstringобязательно

Параметры запроса

  • limitinteger1–100 · default 50
  • cursorstring≤ 2000 chars
  • status"new" | "in_progress" | "done" | "spam"
  • fromstring (date-time)
  • tostring (date-time)
  • qstring≤ 200 chars

Ответ

200FormResponseList

curl "https://api.gravi.page/v1/forms/{formId}/responses" \
  -H "Authorization: Bearer $GRAVI_API_KEY"
PATCH/responses/{responseId}

Update a response

Право: responses:write

Status, read flag and the internal note (an empty note removes it).

Параметры пути

  • responseIdstringобязательно

Тело запроса

  • status"new" | "in_progress" | "done" | "spam"
  • notestring≤ 2000 chars
  • readboolean

Ответ

200FormResponse

curl -X PATCH "https://api.gravi.page/v1/responses/{responseId}" \
  -H "Authorization: Bearer $GRAVI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"status":"done","read":true,"note":"Replied by email"}'
DELETE/responses/{responseId}

Delete a response

Право: responses:write

Параметры пути

  • responseIdstringобязательно

Ответ

204Без содержимого

curl -X DELETE "https://api.gravi.page/v1/responses/{responseId}" \
  -H "Authorization: Bearer $GRAVI_API_KEY"
POST/forms/{formId}/responses/bulk

Change or delete many responses

Право: responses:write

Up to 100 ids, or every response matching filter (the first 200 now, the rest in the background: pending: true).

Параметры пути

  • formIdstringобязательно

Тело запроса

  • idsarray of string1–100 items
  • filterobject
    • status"new" | "in_progress" | "done" | "spam"
    • fromstring (date-time)
    • tostring (date-time)
    • qstring≤ 200 chars
  • action"setStatus" | "markRead" | "markUnread" | "delete"обязательно
  • status"new" | "in_progress" | "done" | "spam"

Ответ

200BulkResult

curl -X POST "https://api.gravi.page/v1/forms/{formId}/responses/bulk" \
  -H "Authorization: Bearer $GRAVI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"filter":{"status":"new"},"action":"markRead"}'
GET/pages/{pageId}/analytics

Get page analytics

Право: analytics:read

Totals, a daily series and the most clicked blocks and links for UTC days from–to (default: the last 30 days). Days before the plan's history are clamped (clamped: true).

Параметры пути

  • pageIdstringобязательно

    Page id (from GET /v1/pages).

Параметры запроса

  • fromstring (date)
  • tostring (date)

Ответ

200Analytics

curl "https://api.gravi.page/v1/pages/{pageId}/analytics" \
  -H "Authorization: Bearer $GRAVI_API_KEY"
GET/openapi.json

This OpenAPI document

Право: Без ключа

Ответ

200application/json

curl "https://api.gravi.page/v1/openapi.json"

Схемы

Error

Every error uses this envelope.

  • errorobjectобязательно
    • codestringобязательно

      Stable code to branch on, e.g. VALIDATION

    • messagestringобязательно
    • detailsmap of any

Project

  • idstringобязательно
  • namestringобязательно
  • planstringобязательно
  • scopesarray of stringобязательно

    Scopes of the calling key

  • limitsmap of number | booleanобязательно
  • usageobjectобязательно
    • pagesintegerобязательно≥ 0
    • publishedPagesintegerобязательно≥ 0
    • storageBytesintegerобязательно≥ 0
  • urlsobjectобязательно
    • dashboardstringобязательно
    • pagesarray of stringобязательно
  • blockTypesarray of objectобязательно
    • typestringобязательно
    • sizesarray of stringобязательно
    • defaultSizestringобязательно

      A size key such as compact or wide (see blockTypes)

    • entitlementstring

PageSummary

  • idstringобязательно
  • slugstringобязательно
  • urlstringобязательно
  • status"published" | "unpublished"обязательно
  • unpublishedReasonstring или nullобязательно
  • titlestringобязательно
  • locale"en" | "ru"обязательно
  • blockCountintegerобязательно≥ 0
  • versionintegerобязательно≥ 0
  • createdAtstring (date-time) или nullобязательно
  • updatedAtstring (date-time) или nullобязательно
  • publishedAtstring (date-time) или nullобязательно

PageList

  • dataarray of PageSummaryобязательно
  • nextCursorstring или nullобязательно

Page

  • idstringобязательно
  • slugstringобязательно
  • urlstringобязательно
  • status"published" | "unpublished"обязательно
  • unpublishedReasonstring или nullобязательно
  • titlestringобязательно
  • locale"en" | "ru"обязательно
  • blockCountintegerобязательно≥ 0
  • versionintegerобязательно≥ 0
  • createdAtstring (date-time) или nullобязательно
  • updatedAtstring (date-time) или nullобязательно
  • publishedAtstring (date-time) или nullобязательно
  • seoobjectобязательно
    • titlestring или nullобязательно
    • descriptionstring или nullобязательно
    • noindexbooleanобязательно
  • headerobjectобязательно
    • titlestringобязательно
    • bioanyобязательно

      Rich text JSON (bio profile) or null

    • bioMarkdownstringобязательно
    • avatarobject или nullобязательно
      • fileIdstringобязательно
      • urlstringобязательно
      • widthnumber
      • heightnumber
  • thememap of anyобязательно
  • blocksarray of Block

Block

  • idstringобязательно
  • typestringобязательно
  • paramsmap of anyобязательно

    Stored block parameters

  • markdownstring

    text / article: the content as Markdown

  • sizeobjectобязательно
    • desktopstring или nullобязательно
    • mobilestring или nullобязательно
  • layoutobjectобязательно
    • desktopobjectобязательно
      • xintegerобязательно≥ 0
      • yintegerобязательно≥ 0
      • wintegerобязательно≥ 0
      • hintegerобязательно≥ 0
    • mobileobjectобязательно
      • xintegerобязательно≥ 0
      • yintegerобязательно≥ 0
      • wintegerобязательно≥ 0
      • hintegerобязательно≥ 0
  • lockedbooleanобязательно
  • lockIdstring или nullобязательно

    The block's password lock, if any

  • filesmap of objectобязательно

    Signed URLs of the files it uses

  • updatedAtstring (date-time) или nullобязательно
  • metadata"pending"

    POST: link metadata is being fetched in the background

BlockList

  • dataarray of Blockобязательно
  • nextCursorstring или nullобязательно

BlockOrderResult

  • dataarray of Blockобязательно

File

  • fileIdstringобязательно
  • urlstringобязательно

Form

  • idstringобязательно
  • titlestring или nullобязательно
  • pageIdstringобязательно
  • pageSlugstringобязательно
  • blockIdstringобязательно
  • acceptingbooleanобязательно
  • responseCountintegerобязательно≥ 0

    Every status but spam

  • unreadCountintegerобязательно≥ 0
  • statusCountsobjectобязательно
    • newintegerобязательно≥ 0
    • in_progressintegerобязательно≥ 0
    • doneintegerобязательно≥ 0
    • spamintegerобязательно≥ 0
  • columnsarray of objectобязательно
    • fieldIdstringобязательно
    • labelstringобязательно
    • type"short_text" | "long_text" | "email" | "phone" | "number" | "url" | "choice" | "date" | "time" | "rating" | "consent"обязательно
    • removedbooleanобязательно
  • lastResponseAtstring (date-time) или nullобязательно
  • blockRemovedAtstring (date-time) или nullобязательно

    Set once the form block was deleted

  • createdAtstring (date-time)обязательно

    ISO 8601, UTC

FormList

  • dataarray of Formобязательно
  • nextCursorstring или nullобязательно

FormResponse

  • idstringобязательно
  • formIdstringобязательно
  • status"new" | "in_progress" | "done" | "spam"обязательно
  • readbooleanобязательно
  • notestring или nullобязательно
  • answersarray of objectобязательно
    • fieldIdstringобязательно
    • labelstringобязательно
    • type"short_text" | "long_text" | "email" | "phone" | "number" | "url" | "choice" | "date" | "time" | "rating" | "consent"обязательно
    • valuestring | array of string | number | booleanобязательно
  • metaobjectобязательно
    • referrerHoststring
    • utmmap of string
    • device"mobile" | "tablet" | "desktop"обязательно
    • locale"en" | "ru"обязательно
  • createdAtstring (date-time)обязательно

    ISO 8601, UTC

  • updatedAtstring (date-time)обязательно

    ISO 8601, UTC

FormResponseList

  • dataarray of FormResponseобязательно
  • nextCursorstring или nullобязательно

BulkResult

  • affectedintegerобязательно≥ 0

    Responses changed or deleted by this call

  • pendingbooleanобязательно

    A filter's remaining matches continue in the background

Analytics

  • pageIdstringобязательно
  • fromstring (date)обязательно

    First UTC day, after clamping

  • tostring (date)обязательно

    Last UTC day (inclusive)

  • clampedbooleanобязательно

    from was moved to the plan's first visible day

  • analyticsDaysintegerобязательно≥ 0

    Days of history the plan shows

  • totalsobjectобязательно
    • viewsintegerобязательно≥ 0
    • uniquesintegerобязательно≥ 0
    • clicksintegerобязательно≥ 0
  • dailyarray of objectобязательно
    • datestring (date)обязательно
    • viewsintegerобязательно≥ 0
    • uniquesintegerобязательно≥ 0
    • clicksintegerобязательно≥ 0
  • topBlocksarray of objectобязательно
    • blockIdstringобязательно
    • typestring или nullобязательно
    • labelstringобязательно
    • clicksintegerобязательно≥ 0
  • headerClicksintegerобязательно≥ 0
  • linksarray of objectобязательно

    Clicked URLs (top 20, the rest in other)

    • keystringобязательно
    • countintegerобязательно≥ 0
  • referrersarray of objectобязательно
    • keystringобязательно
    • countintegerобязательно≥ 0
  • countriesarray of objectобязательно
    • keystringобязательно
    • countintegerобязательно≥ 0
  • devicesarray of objectобязательно
    • keystringобязательно
    • countintegerобязательно≥ 0