Signed HTTP callbacks for vault events, and integrations built on them. Granite posts a JSON `WebhookEvent` to your URL within seconds of each change. Integrations connect a service to one vault so it can act on notes and folders. Chat apps (`slack`, `teams`, `mattermost`, `discord`, `telegram`, `rocket_chat`, `zulip`) take commands; automation tools (`n8n`, `pipedream`, `zapier`, `make`, `ifttt`, `activepieces`, `power_automate`, `webhook`) and `shortcuts` take actions. Automation tools can, optionally, start a workflow when a note is added, moved, or deleted (`note.created`, `note.moved`, `note.deleted`): the signed `WebhookEvent` JSON described below, posted to their `url`. Edits never start an integration's workflow; only a custom `webhook` may subscribe to `note.updated` or `*`. Chat apps get no events. Which kinds a plan may use, and how many per vault, is in `GET /v1/me/entitlements` (`integrations`, `integrations_per_vault`); custom webhooks need a paid plan. An integration's addresses and secrets are stored encrypted and never shown again. Commands and actions go to the integration's `inbound_url` (`POST /v1/integrations/{id}/inbound`) and run as the person who installed it, in its vault only, limited to its `actions`. Chat commands: `new ` (a line break or ` | ` starts the text), `append | `, `daily `, `search `, `list `, `read `, `replace | `, `move | `, `delete ` (a folder with a trailing `/`), and plain text, which is captured to `/Inbox.md`. Each service proves itself its own way: Slack's signing secret, Discord's Ed25519 key, Telegram's secret token (and only the connected chat), Teams' HMAC, or the token of a Mattermost, Rocket.Chat, or Zulip webhook. Automation tools send JSON with the `action_key` as `Authorization: Bearer …` (or `?key=`): `{"action": "create", "title": "…", "content": "…", "folder": "…"}`, and likewise `append`, `daily`, `capture`, `search` (`query`), `list` (`folder`), `read`, `update`, `move` (`path` or `folder`, and `to`), `delete` (`path` or `folder`). A change made this way is not sent back to the same integration as a trigger. Until its secrets are saved (`ready`), an integration answers nothing. AI meeting notetakers send each finished meeting to `inbound_url` with their own webhook, and Granite saves it as a note in the integration's `folder` (default `Meetings`): summary, action items as tasks, attendees, a link to the recording, and the transcript in a folded callout; YAML frontmatter has `type: meeting`, `source`, `date`, `attendees`, and `meeting_id`. The same meeting sent again replaces its own note. Requests are checked with the service's signing secret (`signing_secret`): Read.ai's `X-Read-Signature`, Fathom's and Granola's Standard Webhooks signature (`whsec_…`), Fireflies' `X-Hub-Signature`, Circleback's `X-Signature`, Avoma's `X-Avoma-Signature`, or MeetGeek's `X-MG-Signature`. tl;dv sends the `action_key` as a header, Otter as `?key=`. Fireflies, Granola, and MeetGeek send only the meeting's ID; Granite fetches it with the API key saved as `inbound_token`. Answer with any 2xx status within 10 seconds; other answers and timeouts are retried after 1 min, 5 min, 30 min, 2 h, 6 h, and 24 h, then the delivery is marked failed (`410 Gone` stops retries at once). Every attempt is in the delivery log and any delivery can be replayed. Delivery is at least once: skip event `id`s you have already handled. Events: `note.created`, `note.updated`, `note.moved`, `note.deleted`; `vault.shared` and `member.added`/`member.removed`/`member.role_changed` start with sharing. Subscribe with exact names, `note.*`, `member.*`, or `*`. Headers: `Granite-Event`, `Granite-Delivery`, `Granite-Webhook`, and `Granite-Signature: t=,v1=`, where the signature is the hex HMAC-SHA256 of `.` keyed with the webhook secret (`whsec_...`). Check it against the raw body before parsing, and reject timestamps more than 5 minutes old. Node.js: ```js import { createHmac, timingSafeEqual } from "node:crypto"; function verify(secret, header, rawBody) { const parts = Object.fromEntries(header.split(",").map((p) => p.split("="))); if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false; const want = createHmac("sha256", secret).update(`${parts.t}.${rawBody}`).digest("hex"); return timingSafeEqual(Buffer.from(want), Buffer.from(parts.v1 ?? "")); } ``` Python: ```python import hashlib, hmac, time def verify(secret: str, header: str, raw_body: bytes) -> bool: parts = dict(p.split("=", 1) for p in header.split(",")) if abs(time.time() - int(parts["t"])) > 300: return False want = hmac.new(secret.encode(), parts["t"].encode() + b"." + raw_body, hashlib.sha256).hexdigest() return hmac.compare_digest(want, parts.get("v1", "")) ``` Go: ```go func verify(secret, header string, body []byte) bool { var t, sig string for _, p := range strings.Split(header, ",") { k, v, _ := strings.Cut(p, "=") if k == "t" { t = v } else if k == "v1" { sig = v } } ts, err := strconv.ParseInt(t, 10, 64) if err != nil || math.Abs(time.Since(time.Unix(ts, 0)).Seconds()) > 300 { return false } m := hmac.New(sha256.New, []byte(secret)) m.Write([]byte(t + ".")) m.Write(body) return hmac.Equal([]byte(hex.EncodeToString(m.Sum(nil))), []byte(sig)) } ```
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Get an API key
Free tier, no form to fill in. Signing in shares your email address with us — we
store it to create your key and to recognise you if you sign in with another
provider. See our Privacy Policy and
Terms.
openapi: 3.2.0
info:
title: Granite Webhooks API
version: 1.0.0
summary: REST API for Granite clients, bots, and integrations.
description: 'Conventions (apply to every operation; see PLAN.md 3.2 and 3.6):
- **Base URL** `https://api.granite.md`. All public operations live under `/v1`.
After Phase 6 ships, `/v1` changes are additive only.
- **Errors** use RFC 9457 problem details (`application/problem+json`, schema `Problem`).
The `type` URI is `https://granite.md/problems/`; `code` refines it for programmatic handling.
Every response carries `X-Request-ID`, also echoed as `request_id` in problems.
- **Pagination** is cursor based. List operations accept `cursor` and `limit` and return
`{ "items": [...], "next_cursor": "..." }`. `next_cursor` is absent on the last page.
Cursors are opaque; clients must not parse or build them.
- **Idempotency**: non-idempotent `POST` operations accept an `Idempotency-Key` header
(1-255 printable ASCII). Repeating a request with the same key and body within 24 hours
returns the original response; the same key with a different body returns `409` with
code `idempotency_key_reused`.
- **IDs** are UUIDv7 strings. **Timestamps** are RFC 3339 in UTC.
- **Authentication**: `Authorization: Bearer ` with a session access token,
a personal API token (`gr_pat_...`), or an OAuth access token. Scopes are listed per operation.
- **Rate limits**: each personal API token may make 600 requests a minute. Over the limit
the API answers `429` with code `token_rate_limited` and a `Retry-After` header (seconds).'
license:
name: Proprietary
identifier: LicenseRef-Proprietary
servers:
- url: https://api.granite.md
description: Production
- url: http://localhost:8080
description: Local development
security:
- bearerAuth: []
tags:
- name: Webhooks
description: 'Signed HTTP callbacks for vault events, and integrations built on them. Granite posts a
JSON `WebhookEvent` to your URL within seconds of each change. Integrations connect a service to one vault so it can act on notes and folders. Chat
apps (`slack`, `teams`, `mattermost`, `discord`, `telegram`, `rocket_chat`, `zulip`)
take commands; automation tools (`n8n`, `pipedream`, `zapier`, `make`, `ifttt`,
`activepieces`, `power_automate`, `webhook`) and `shortcuts` take actions. Automation
tools can, optionally, start a workflow when a note is added, moved, or deleted
(`note.created`, `note.moved`, `note.deleted`): the signed `WebhookEvent` JSON described
below, posted to their `url`. Edits never start an integration''s workflow; only a custom
`webhook` may subscribe to `note.updated` or `*`. Chat apps get no events. Which kinds a plan may use, and
how many per vault, is in `GET /v1/me/entitlements` (`integrations`,
`integrations_per_vault`); custom webhooks need a paid plan. An integration''s
addresses and secrets are stored encrypted and never shown again. Commands and actions go to the integration''s `inbound_url`
(`POST /v1/integrations/{id}/inbound`) and run as the person who installed it, in its
vault only, limited to its `actions`. Chat commands: `new ` (a line break
or ` | ` starts the text), `append | `, `daily `, `search `,
`list `, `read `, `replace | `, `move |
`, `delete ` (a folder with a trailing `/`), and plain text,
which is captured to `/Inbox.md`. Each service proves itself its own way:
Slack''s signing secret, Discord''s Ed25519 key, Telegram''s secret token (and only the
connected chat), Teams'' HMAC, or the token of a Mattermost, Rocket.Chat, or Zulip
webhook. Automation tools send JSON with the `action_key` as `Authorization: Bearer …`
(or `?key=`): `{"action": "create", "title": "…", "content": "…", "folder": "…"}`, and
likewise `append`, `daily`, `capture`, `search` (`query`), `list` (`folder`), `read`,
`update`, `move` (`path` or `folder`, and `to`), `delete` (`path` or `folder`). A change
made this way is not sent back to the same integration as a trigger. Until its secrets
are saved (`ready`), an integration answers nothing. AI meeting notetakers send each finished meeting to `inbound_url` with their own
webhook, and Granite saves it as a note in the integration''s `folder` (default
`Meetings`): summary, action items as tasks, attendees, a link to the recording, and the
transcript in a folded callout; YAML frontmatter has `type: meeting`, `source`, `date`,
`attendees`, and `meeting_id`. The same meeting sent again replaces its own note. Requests are checked with the service''s signing secret (`signing_secret`): Read.ai''s
`X-Read-Signature`, Fathom''s and Granola''s Standard Webhooks signature (`whsec_…`),
Fireflies'' `X-Hub-Signature`, Circleback''s `X-Signature`, Avoma''s `X-Avoma-Signature`,
or MeetGeek''s `X-MG-Signature`. tl;dv sends the `action_key` as a header, Otter as
`?key=`. Fireflies, Granola, and MeetGeek send only the meeting''s ID; Granite fetches it
with the API key saved as `inbound_token`. Answer with any 2xx status within 10 seconds; other answers and timeouts are retried after 1 min, 5 min, 30 min,
2 h, 6 h, and 24 h, then the delivery is marked failed (`410 Gone` stops retries at
once). Every attempt is in the delivery log and any delivery can be replayed. Delivery
is at least once: skip event `id`s you have already handled. Events: `note.created`, `note.updated`, `note.moved`, `note.deleted`; `vault.shared`
and `member.added`/`member.removed`/`member.role_changed` start with sharing. Subscribe with exact names, `note.*`, `member.*`, or `*`. Headers: `Granite-Event`, `Granite-Delivery`, `Granite-Webhook`, and
`Granite-Signature: t=,v1=`, where the signature is the hex
HMAC-SHA256 of `.` keyed with the webhook secret (`whsec_...`). Check it
against the raw body before parsing, and reject timestamps more than 5 minutes old.'
paths:
/v1/vaults/{vault_id}/webhooks:
parameters:
- $ref: '#/components/parameters/VaultID'
get:
operationId: listWebhooks
tags:
- Webhooks
summary: List a vault's webhooks
description: Needs administrator access to the vault.
responses:
'200':
description: Webhooks.
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookList'
default:
$ref: '#/components/responses/Error'
post:
operationId: createWebhook
tags:
- Webhooks
summary: Add a webhook or integration
description: 'The kind must be in the plan (403 `plan_limit_integration`, or `plan_limit_webhooks`
for custom webhooks), and the vault under the plan''s `integrations_per_vault` (403
`too_many_webhooks`). Events from now on are delivered. The secret is returned only
here and when rotated.'
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookCreate'
responses:
'201':
description: The webhook and its secret.
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookWithSecret'
default:
$ref: '#/components/responses/Error'
/v1/vaults/{vault_id}/webhooks/{webhook_id}:
parameters:
- $ref: '#/components/parameters/VaultID'
- $ref: '#/components/parameters/WebhookID'
get:
operationId: getWebhook
tags:
- Webhooks
summary: Get a webhook
responses:
'200':
description: The webhook.
content:
application/json:
schema:
$ref: '#/components/schemas/Webhook'
default:
$ref: '#/components/responses/Error'
patch:
operationId: updateWebhook
tags:
- Webhooks
summary: Change a webhook
description: Omitted fields keep their value. `rotate_secret` returns a new secret once.
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookUpdate'
responses:
'200':
description: The webhook (with `secret` when it was rotated).
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookWithSecret'
default:
$ref: '#/components/responses/Error'
delete:
operationId: deleteWebhook
tags:
- Webhooks
summary: Delete a webhook
description: Its pending deliveries and delivery log are deleted too.
responses:
'204':
description: Deleted.
default:
$ref: '#/components/responses/Error'
/v1/vaults/{vault_id}/webhooks/{webhook_id}/connect:
parameters:
- $ref: '#/components/parameters/VaultID'
- $ref: '#/components/parameters/WebhookID'
post:
operationId: connectIntegration
tags:
- Webhooks
summary: Connect a chat to Granite's bot
description: 'For integrations on Granite''s own bot (`granite_bot`): a one-time code, valid for 30
minutes, and the link that carries it (Telegram''s start link, Slack''s or Discord''s
install page). The chat it is used in, or where `link CODE` is typed, is connected.'
responses:
'201':
description: The code and link.
content:
application/json:
schema:
$ref: '#/components/schemas/IntegrationConnect'
default:
$ref: '#/components/responses/Error'
/v1/vaults/{vault_id}/webhooks/{webhook_id}/chats/{chat_id}:
parameters:
- $ref: '#/components/parameters/VaultID'
- $ref: '#/components/parameters/WebhookID'
- name: chat_id
in: path
required: true
schema:
type: string
format: uuid
delete:
operationId: disconnectIntegrationChat
tags:
- Webhooks
summary: Disconnect a chat
description: Commands from the chat no longer reach Granite.
responses:
'204':
description: Disconnected.
default:
$ref: '#/components/responses/Error'
/v1/vaults/{vault_id}/webhooks/{webhook_id}/test:
parameters:
- $ref: '#/components/parameters/VaultID'
- $ref: '#/components/parameters/WebhookID'
post:
operationId: testWebhook
tags:
- Webhooks
summary: Send a test event
description: Queues a `ping` event, sent right away, so you can check the connection.
responses:
'202':
description: The new delivery.
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookDelivery'
default:
$ref: '#/components/responses/Error'
/v1/vaults/{vault_id}/webhooks/{webhook_id}/deliveries:
parameters:
- $ref: '#/components/parameters/VaultID'
- $ref: '#/components/parameters/WebhookID'
get:
operationId: listWebhookDeliveries
tags:
- Webhooks
summary: The delivery log
description: Deliveries, newest first, with the outcome of their latest attempt.
parameters:
- $ref: '#/components/parameters/Cursor'
- $ref: '#/components/parameters/Limit'
responses:
'200':
description: Deliveries.
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookDeliveryList'
default:
$ref: '#/components/responses/Error'
/v1/vaults/{vault_id}/webhooks/{webhook_id}/deliveries/{delivery_id}/replay:
parameters:
- $ref: '#/components/parameters/VaultID'
- $ref: '#/components/parameters/WebhookID'
- name: delivery_id
in: path
required: true
schema:
type: string
format: uuid
post:
operationId: replayWebhookDelivery
tags:
- Webhooks
summary: Send a delivery again
description: Creates a new delivery of the same event (same event `id`), sent right away.
responses:
'202':
description: The new delivery.
content:
application/json:
schema:
$ref: '#/components/schemas/WebhookDelivery'
default:
$ref: '#/components/responses/Error'
components:
schemas:
WebhookList:
type: object
required:
- items
properties:
items:
type: array
items:
$ref: '#/components/schemas/Webhook'
IntegrationConnect:
type: object
required:
- code
- url
- expires_at
properties:
code:
type: string
description: One-time code (`ABCDE-12345`); typed in a chat as `link CODE`.
url:
type: string
description: Opens the service with the code (Telegram) or adds Granite to it (Slack, Discord).
group_url:
type: string
description: Telegram only, adds the bot to a group with the code.
expires_at:
type: string
format: date-time
WebhookWithSecret:
type: object
required:
- webhook
properties:
webhook:
$ref: '#/components/schemas/Webhook'
secret:
type: string
description: The signing secret (`whsec_...`). Shown only when created or rotated.
action_key:
type: string
description: 'Automation tools: the key for `inbound_url` (`gik_...`). Shown only when actions
are first turned on or the key is rotated.
'
FieldError:
type: object
required:
- field
- detail
properties:
field:
type: string
description: JSON path of the invalid field, for example `ops[2].path`.
detail:
type: string
IntegrationKind:
type: string
description: 'The connected service. `webhook` is a custom endpoint. AI meeting notetakers
(`read_ai`, `fathom`, `fireflies`, `tldv`, `otter`, `granola`, `circleback`, `avoma`,
`meetgeek`) send finished meetings. `google_chat`, `webex`, and `ntfy` could only notify
and can no longer be added.
'
enum:
- slack
- teams
- mattermost
- discord
- telegram
- rocket_chat
- zulip
- shortcuts
- n8n
- pipedream
- zapier
- make
- ifttt
- activepieces
- power_automate
- webhook
- read_ai
- fathom
- fireflies
- tldv
- otter
- granola
- circleback
- avoma
- meetgeek
- google_chat
- webex
- ntfy
Problem:
type: object
description: RFC 9457 problem details.
required:
- type
- title
- status
properties:
type:
type: string
format: uri
examples:
- https://granite.md/problems/conflict
title:
type: string
status:
type: integer
format: int32
detail:
type: string
instance:
type: string
code:
type: string
description: Machine-readable refinement of `type`, for example `version_conflict`.
request_id:
type: string
errors:
type: array
description: Per-field validation errors.
items:
$ref: '#/components/schemas/FieldError'
WebhookUpdate:
type: object
properties:
url:
type: string
maxLength: 2000
bot_token:
type: string
maxLength: 100
chat_id:
type: string
maxLength: 40
actions:
type: array
items:
$ref: '#/components/schemas/IntegrationAction'
folder:
type: string
maxLength: 200
signing_secret:
type: string
maxLength: 200
description: '`slack`: the app''s signing secret (Basic Information); notetakers: the webhook''s signing secret.'
public_key:
type: string
maxLength: 200
description: '`discord`: the application''s public key (hex).'
application_id:
type: string
maxLength: 40
description: '`discord`: the application ID; with `bot_token`, Granite registers the slash commands.'
inbound_token:
type: string
maxLength: 200
description: '`teams`: the outgoing webhook''s security token; `mattermost`, `rocket_chat`, `zulip`: the webhook''s token; `fireflies`, `granola`, `meetgeek`: the API key meetings are fetched with.'
rotate_action_key:
type: boolean
description:
type: string
maxLength: 200
events:
type: array
items:
$ref: '#/components/schemas/WebhookEventType'
active:
type: boolean
rotate_secret:
type: boolean
WebhookCreate:
type: object
required:
- events
properties:
kind:
$ref: '#/components/schemas/IntegrationKind'
url:
type: string
maxLength: 2000
description: 'Automation tools: the https URL that starts the workflow (a catch hook), for
triggers. Chat apps need none.
'
bot_token:
type: string
maxLength: 100
description: '`telegram`: the bot''s token from @BotFather.'
chat_id:
type: string
maxLength: 40
description: '`telegram`: the chat, group, or channel (`-100…` or `@name`) the bot posts to.'
actions:
type: array
items:
$ref: '#/components/schemas/IntegrationAction'
folder:
type: string
maxLength: 200
signing_secret:
type: string
maxLength: 200
description: '`slack`: the app''s signing secret (Basic Information); notetakers: the webhook''s signing secret.'
public_key:
type: string
maxLength: 200
description: '`discord`: the application''s public key (hex).'
application_id:
type: string
maxLength: 40
description: '`discord`: the application ID; with `bot_token`, Granite registers the slash commands.'
inbound_token:
type: string
maxLength: 200
description: '`teams`: the outgoing webhook''s security token; `mattermost`, `rocket_chat`, `zulip`: the webhook''s token; `fireflies`, `granola`, `meetgeek`: the API key meetings are fetched with.'
description:
type: string
maxLength: 200
events:
type: array
items:
$ref: '#/components/schemas/WebhookEventType'
WebhookDelivery:
type: object
required:
- id
- event_id
- event_type
- state
- attempts
- created_at
- payload
properties:
id:
type: string
format: uuid
event_id:
type: string
format: uuid
event_type:
type: string
state:
type: string
enum:
- pending
- succeeded
- failed
attempts:
type: integer
next_attempt_at:
type: string
format: date-time
last_status:
type: integer
description: The HTTP status of the latest attempt (absent if no answer).
last_error:
type: string
replay_of:
type: string
format: uuid
created_at:
type: string
format: date-time
delivered_at:
type: string
format: date-time
payload:
$ref: '#/components/schemas/WebhookEvent'
WebhookDeliveryList:
type: object
required:
- items
properties:
items:
type: array
items:
$ref: '#/components/schemas/WebhookDelivery'
next_cursor:
type: string
IntegrationChat:
type: object
required:
- id
- name
- created_at
properties:
id:
type: string
format: uuid
name:
type: string
description: The chat, channel, or server, as the service named it.
created_at:
type: string
format: date-time
IntegrationAction:
type: string
description: 'What an integration may do: `capture` (plain messages to the inbox note), `create`,
`append`, `daily` (today''s daily note), `search`, `list` (a folder), `read`, `update`
(replace the text), `move` and `delete` (a note or a whole folder), and `meeting`
(notetakers only: save each finished meeting as a note).
'
enum:
- capture
- create
- append
- daily
- search
- list
- read
- update
- move
- delete
- meeting
Webhook:
type: object
required:
- id
- vault_id
- kind
- url
- description
- events
- actions
- folder
- ready
- granite_bot
- active
- created_at
- updated_at
properties:
id:
type: string
format: uuid
vault_id:
type: string
format: uuid
kind:
$ref: '#/components/schemas/IntegrationKind'
url:
type: string
description: 'Where triggers go: a masked copy of the tool''s URL (the real address works as a
password); for Telegram `telegram:<chat id>`; empty for chat apps.
'
description:
type: string
events:
type: array
items:
$ref: '#/components/schemas/WebhookEventType'
actions:
type: array
items:
$ref: '#/components/schemas/IntegrationAction'
folder:
type: string
description: Where new notes and the capture inbox go.
inbound_url:
type: string
description: Where the service sends commands and actions.
ready:
type: boolean
description: Everything requests are checked with is saved, so commands work.
last_inbound_at:
type: string
format: date-time
description: When the service last sent a verified command or action.
granite_bot:
type: boolean
description: 'Telegram, Slack, and Discord: the integration uses Granite''s own bot, so there is
nothing to set up in the service; chats are connected with `connect`.
'
chats:
type: array
description: Granite's bot only, the chats connected to the integration.
items:
$ref: '#/components/schemas/IntegrationChat'
active:
type: boolean
created_at:
type: string
format: date-time
updated_at:
type: string
format: date-time
WebhookEvent:
type: object
description: The JSON body Granite posts.
required:
- id
- type
- created_at
- vault_id
- data
properties:
id:
type: string
format: uuid
description: The event ID; the same in replays and retries.
type:
type: string
created_at:
type: string
format: date-time
vault_id:
type: string
format: uuid
data:
type: object
additionalProperties: true
description: 'For note events: `file_id`, `version_id`, `path`, `old_path` (moves), `kind`
(markdown, text, binary), `size`, `author_id`, `seq`. The text is not included;
read it with the notes API.
'
WebhookEventType:
type: string
enum:
- '*'
- note.*
- member.*
- note.created
- note.updated
- note.moved
- note.deleted
- vault.shared
- member.added
- member.removed
- member.role_changed
headers:
RequestID:
description: Identifier of this request, for support and log correlation.
schema:
type: string
parameters:
VaultID:
name: vault_id
in: path
required: true
schema:
type: string
format: uuid
WebhookID:
name: webhook_id
in: path
required: true
schema:
type: string
format: uuid
Limit:
name: limit
in: query
required: false
description: Maximum items to return.
schema:
type: integer
format: int32
minimum: 1
maximum: 200
default: 50
Cursor:
name: cursor
in: query
required: false
description: Opaque cursor from a previous page's `next_cursor`.
schema:
type: string
maxLength: 512
responses:
Error:
description: Error response.
headers:
X-Request-ID:
$ref: '#/components/headers/RequestID'
content:
application/problem+json:
schema:
$ref: '#/components/schemas/Problem'
securitySchemes:
bearerAuth:
type: http
scheme: bearer
description: Session access token, personal API token, or OAuth access token.