/projectGet 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"Gravi API · v1
JSON API для всего, что умеет редактор: создавайте страницы, добавляйте и расставляйте блоки, читайте ответы форм и аналитику. Ключи принадлежат проекту на тарифе Pro.
curl "https://api.gravi.page/v1/project" \
-H "Authorization: Bearer $GRAVI_API_KEY"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" }
}
}| Статус | Коды |
|---|---|
| 401 | UNAUTHENTICATEDACCOUNT_BLOCKEDACCOUNT_DELETED |
| 403 | FORBIDDENPLAN_REQUIREDLIMIT_REACHED |
| 404 | NOT_FOUND |
| 409 | CONFLICTSLUG_TAKENSLUG_RESERVED |
| 422 | SLUG_INVALIDVALIDATION |
| 429 | RATE_LIMITED |
| 502 | FETCH_FAILED |
Списки возвращают { data, nextCursor }. Передавайте limit (1–100, по умолчанию 50) и nextCursor предыдущей страницы как cursor, пока он не станет null.
Сгенерирован из документа OpenAPI. Описания эндпоинтов и полей — на английском, как и сам API.
/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" /pagesПраво: content:read
limitinteger1–100 · default 50cursorstring≤ 200 chars200PageList
curl "https://api.gravi.page/v1/pages" \
-H "Authorization: Bearer $GRAVI_API_KEY" /pagesПраво: content:write
Creates a published page from an optional template.
slugstringобязательно1–200 charstemplate"blank" | "creator" | "developer" | "business"titlestring≤ 80 chars201Page
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"}' /pages /{pageId}Право: content:read
pageIdstringобязательноPage id (from GET /v1/pages).
200Page
curl "https://api.gravi.page/v1/pages/{pageId}" \
-H "Authorization: Bearer $GRAVI_API_KEY" /pages /{pageId}Право: content:write
pageIdstringобязательноPage id (from GET /v1/pages).
slugstring1–200 charsstatus"published" | "unpublished"seoobjecttitlestring или null≤ 70 charsdescriptionstring или null≤ 200 charsnoindexbooleanlocale"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"}}' /pages /{pageId}Право: 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" /pages /{pageId} /headerПраво: content:write
bio takes Markdown; avatarFileId: null removes the avatar.
pageIdstringобязательноPage id (from GET /v1/pages).
titlestring≤ 80 charsbiostring≤ 40000 charsbioJsonanyavatarFileIdstring или null1–64 chars200Page
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)."}' /pages /{pageId} /themeПраво: content:write
pageIdstringобязательноPage id (from GET /v1/pages).
presetstringобязательно1–64 charsoverridesmap of any200Page
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"}' /pages /{pageId} /blocksПраво: content:read
In desktop reading order.
pageIdstringобязательноPage id (from GET /v1/pages).
limitinteger1–100 · default 50cursorstring≤ 200 chars200BlockList
curl "https://api.gravi.page/v1/pages/{pageId}/blocks" \
-H "Authorization: Bearer $GRAVI_API_KEY" /pages /{pageId} /blocksПраво: 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).
idstringtypestringобязательно1–32 charsparamsmap of anyurlstring1–2048 charsmarkdownstring≤ 40000 charsimageUrlstring1–2048 charsfileIdstring1–64 charssize"icon" | "compact" | "small" | "banner" | "wide" | "large" | "auto" | "full" | "full_auto" | objectposition"end" | "start" | "auto" | object201Block
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"}' /pages /{pageId} /blocks /orderПраво: 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 itemsmobilearray of string≤ 1000 itemscurl -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"]}' /pages /{pageId} /blocks /{blockId}Право: 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 anymarkdownstring≤ 40000 charssize"icon" | "compact" | "small" | "banner" | "wide" | "large" | "auto" | "full" | "full_auto" | object200Block
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"}' /pages /{pageId} /blocks /{blockId}Право: 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" /pages /{pageId} /blocks /{blockId} /lockПраво: 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 charshintstring≤ 80 charslockIdstringобязательно1–64 chars200Block
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"}' /pages /{pageId} /blocks /{blockId} /lockПраво: 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" /filesПраво: 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 chars201File
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"}' /formsПраво: responses:read
Newest response first. Forms whose block was deleted keep their responses.
limitinteger1–100 · default 50cursorstring≤ 200 chars200FormList
curl "https://api.gravi.page/v1/forms" \
-H "Authorization: Bearer $GRAVI_API_KEY" /forms /{formId} /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 50cursorstring≤ 2000 charsstatus"new" | "in_progress" | "done" | "spam"fromstring (date-time)tostring (date-time)qstring≤ 200 charscurl "https://api.gravi.page/v1/forms/{formId}/responses" \
-H "Authorization: Bearer $GRAVI_API_KEY" /responses /{responseId}Право: responses:write
Status, read flag and the internal note (an empty note removes it).
responseIdstringобязательноstatus"new" | "in_progress" | "done" | "spam"notestring≤ 2000 charsreadboolean200FormResponse
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"}' /responses /{responseId}Право: responses:write
responseIdstringобязательно204Без содержимого
curl -X DELETE "https://api.gravi.page/v1/responses/{responseId}" \
-H "Authorization: Bearer $GRAVI_API_KEY" /forms /{formId} /responses /bulkПраво: 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 itemsfilterobjectstatus"new" | "in_progress" | "done" | "spam"fromstring (date-time)tostring (date-time)qstring≤ 200 charsaction"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"}' /pages /{pageId} /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" /openapi.jsonПраво: Без ключа
200application/json
curl "https://api.gravi.page/v1/openapi.json"Every error uses this envelope.
errorobjectобязательноcodestringобязательноStable code to branch on, e.g. VALIDATION
messagestringобязательноdetailsmap of anyidstringобязательноnamestringобязательноplanstringобязательноscopesarray of stringобязательноScopes of the calling key
limitsmap of number | booleanобязательноusageobjectобязательноpagesintegerобязательно≥ 0publishedPagesintegerобязательно≥ 0storageBytesintegerобязательно≥ 0urlsobjectобязательноdashboardstringобязательноpagesarray of stringобязательноblockTypesarray of objectобязательноtypestringобязательноsizesarray of stringобязательноdefaultSizestringобязательноA size key such as compact or wide (see blockTypes)
entitlementstringidstringобязательноslugstringобязательноurlstringобязательноstatus"published" | "unpublished"обязательноunpublishedReasonstring или nullобязательноtitlestringобязательноlocale"en" | "ru"обязательноblockCountintegerобязательно≥ 0versionintegerобязательно≥ 0createdAtstring (date-time) или nullобязательноupdatedAtstring (date-time) или nullобязательноpublishedAtstring (date-time) или nullобязательноnextCursorstring или nullобязательноidstringобязательноslugstringобязательноurlstringобязательноstatus"published" | "unpublished"обязательноunpublishedReasonstring или nullобязательноtitlestringобязательноlocale"en" | "ru"обязательноblockCountintegerобязательно≥ 0versionintegerобязательно≥ 0createdAtstring (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обязательноwidthnumberheightnumberthememap of anyобязательноblocksarray of Blockidstringобязательноtypestringобязательноparamsmap of anyобязательноStored block parameters
markdownstringtext / article: the content as Markdown
sizeobjectобязательноdesktopstring или nullобязательноmobilestring или nullобязательноlayoutobjectобязательноdesktopobjectобязательноxintegerобязательно≥ 0yintegerобязательно≥ 0wintegerобязательно≥ 0hintegerобязательно≥ 0mobileobjectобязательноxintegerобязательно≥ 0yintegerобязательно≥ 0wintegerобязательно≥ 0hintegerобязательно≥ 0lockedbooleanобязательно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
nextCursorstring или nullобязательноfileIdstringобязательноurlstringобязательноidstringобязательноtitlestring или nullобязательноpageIdstringобязательноpageSlugstringобязательноblockIdstringобязательноacceptingbooleanобязательноresponseCountintegerобязательно≥ 0Every status but spam
unreadCountintegerобязательно≥ 0statusCountsobjectобязательноnewintegerобязательно≥ 0in_progressintegerобязательно≥ 0doneintegerобязательно≥ 0spamintegerобязательно≥ 0columnsarray 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
nextCursorstring или nullобязательно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обязательноreferrerHoststringutmmap of stringdevice"mobile" | "tablet" | "desktop"обязательноlocale"en" | "ru"обязательноcreatedAtstring (date-time)обязательноISO 8601, UTC
updatedAtstring (date-time)обязательноISO 8601, UTC
nextCursorstring или nullобязательноaffectedintegerобязательно≥ 0Responses changed or deleted by this call
pendingbooleanобязательноA filter's remaining matches continue in the background
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обязательно≥ 0Days of history the plan shows
totalsobjectобязательноviewsintegerобязательно≥ 0uniquesintegerобязательно≥ 0clicksintegerобязательно≥ 0dailyarray of objectобязательноdatestring (date)обязательноviewsintegerобязательно≥ 0uniquesintegerобязательно≥ 0clicksintegerобязательно≥ 0topBlocksarray of objectобязательноblockIdstringобязательноtypestring или nullобязательноlabelstringобязательноclicksintegerобязательно≥ 0headerClicksintegerобязательно≥ 0linksarray of objectобязательноClicked URLs (top 20, the rest in other)
keystringобязательноcountintegerобязательно≥ 0referrersarray of objectобязательноkeystringобязательноcountintegerобязательно≥ 0countriesarray of objectобязательноkeystringобязательноcountintegerобязательно≥ 0devicesarray of objectобязательноkeystringобязательноcountintegerобязательно≥ 0