Partners
Partner API reference
Every partner endpoint, with its auth, request, responses, errors, and quotas.
The partner API lives under /api/v1/partner on your Revyl API base URL, such as
https://backend.revyl.ai. To get a partner API key, email
support@revyl.ai.
Requests
- Auth:
Authorization: Bearer <partner API key>on every request. The key identifies your platform; the account comes from the path or body. Keep the key on your servers. - Accounts:
{external_account_id}is your ID for a customer account: 1 to 128 letters, digits,_, or-. - Request IDs: send a UUID in
X-Request-Id, and Revyl returns it on the response for support. - Caching: responses that carry a credential or a dashboard link have
Cache-Control: no-store. Don't log or cache them.
Errors
Every error the API defines has one shape. Branch on error.code, not on the message:
{
"error": {
"code": "not_connected",
"message": "There's no connection for this external account."
}
}An already_connected error also carries connection. An unexpected 500 has no error.code;
retry it with backoff.
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_request | The request failed validation. |
| 400 | authorization_pending, slow_down, access_denied, expired_token | Approval polling outcomes. See Wait for approval. |
| 401 | invalid_partner_credential | The partner key is missing, invalid, or revoked. |
| 403 | partner_not_allowed | Your integration, or this account, isn't enabled in this environment. |
| 403 | not_supported_for_connection | This operation isn't available for this connection. |
| 404 | not_connected | There's no connection for this account. |
| 409 | already_connected | The account is already connected. |
| 409 | connection_in_progress | Another request for this account is running. Retry after Retry-After. |
| 409 | needs_support | Revyl found an inconsistent state. Contact support@revyl.ai. |
| 429 | rate_limited | Too many requests this minute. Retry after Retry-After. |
| 429 | daily_limit_reached | A daily cap is spent. It resets at 00:00 UTC. |
| 503 | temporarily_unavailable | Retry after Retry-After, with bounded backoff. |
Quotas
Requests count against two per-minute buckets: one for your platform and one for each account. Responses report each bucket that was checked:
| Header | Meaning |
|---|---|
X-RateLimit-Limit-Partner, X-RateLimit-Limit-Account | Requests allowed per UTC minute. |
X-RateLimit-Remaining-Partner, X-RateLimit-Remaining-Account | Requests left this minute. |
X-RateLimit-Reset-Partner, X-RateLimit-Reset-Account | When the window resets, in Unix seconds. |
A missing header doesn't mean unlimited. The values are snapshots, so pace your requests and
honor Retry-After on a 429. Two daily caps also reset at 00:00 UTC: new workspaces Revyl
creates for your platform, and invitations per connection.
Endpoints
Connect an account
POST /api/v1/partner/connections
Body: external_account_id, external_account_name, organization_name, owner_email, and
optional mode (auto or link_existing). Names are 1 to 200 characters.
| Status | Body |
|---|---|
201 | connection (state: "connected") and credential |
202 | connection (state: "awaiting_approval") and approval |
Errors include 403 partner_not_allowed, 409 already_connected, 409 connection_in_progress,
409 needs_support, and 429 daily_limit_reached.
Collect an approved credential
POST /api/v1/partner/connections/{external_account_id}/approval/token
No body. 200 returns connection and credential, once. Until then it answers one of the
400 polling codes.
Get a connection
GET /api/v1/partner/connections/{external_account_id}
200 returns the connection:
{
"external_account_id": "acct_123",
"state": "connected",
"organization": { "id": "<organization UUID>", "name": "Example Workspace" },
"origin": "provisioned"
}state is connected, awaiting_approval, or not_connected. organization is present only
while connected. origin is provisioned when Revyl created the workspace and existing when
an admin linked one.
Rotate the connection credential
POST /api/v1/partner/connections/{external_account_id}/credentials
No body. 201 returns credential: type: "api_key", secret, and expires_at. The
replaced credential keeps working for a short grace period; older ones and session credentials stop
at once. Errors include 404 not_connected and
409 connection_in_progress.
Mint a session credential
POST /api/v1/partner/connections/{external_account_id}/session-credentials
Optional body: { "ttl_seconds": 7200 }, from 60 to 14,400. 201 returns credential_id and
credential: type: "session_token", secret, and expires_at. Errors include
404 not_connected.
Revoke a session credential
DELETE /api/v1/partner/connections/{external_account_id}/session-credentials/{credential_id}
204. An unknown ID also answers 204. It can also answer 409 connection_in_progress.
Invite a teammate
POST /api/v1/partner/connections/{external_account_id}/invitations
Body: { "email": "sam@example.com" }. 202 returns { "status": "sent" }, and the person
joins as a Member. Revyl also answers 202 when it already invited that address this UTC day,
without sending again. Errors include 400 invalid_request when Revyl rejects the invitation,
403 not_supported_for_connection for a linked organization, 404 not_connected,
409 connection_in_progress, and 429 daily_limit_reached.
Request a dashboard link
POST /api/v1/partner/connections/{external_account_id}/dashboard-links
No body. 200 returns url, requires_login: true, and expires_at: null. See
Dashboard links.
Read usage
GET /api/v1/partner/connections/{external_account_id}/usage?from=<ISO time>&to=<ISO time>
from and to are ISO 8601 times with a time zone, such as 2026-10-01T00:00:00Z.
200 returns period and usage: one row per feature, with seconds for device and build
time and millicents for AI usage, for work started through the account's connections. The
period is [from, to) and at most 93 days. Errors include 400 invalid_request and
404 not_connected for an account that was never connected.
Disconnect an account
DELETE /api/v1/partner/connections/{external_account_id}
204. Revokes the connection's credentials and session credentials and stops the live sessions
started with them. App embeds keep working until you revoke them with
DELETE /api/v1/embeds/{embed_id}. Safe to repeat. A 503 means something couldn't be stopped yet; retry to finish.