mirror of
https://github.com/warmbly/warmbly.git
synced 2026-10-03 08:02:04 +00:00
530 lines
21 KiB
Plaintext
530 lines
21 KiB
Plaintext
---
|
|
title: Error codes
|
|
description: Reference for all API error codes and their meanings.
|
|
---
|
|
|
|
The Warmbly API uses standard HTTP status codes and returns structured error responses in JSON format.
|
|
|
|
## Error response format
|
|
|
|
All errors follow this structure:
|
|
|
|
```json
|
|
{
|
|
"error": "Error Type",
|
|
"message": "Human-readable description of what went wrong.",
|
|
"code": "machine_readable_code",
|
|
"request_id": "req_or_uuid_for_support"
|
|
}
|
|
```
|
|
|
|
`error` and `message` are for people. Client logic should use `code`, HTTP status, and endpoint-specific fields such as `retry_after`. Include `request_id` when contacting support.
|
|
|
|
## HTTP status codes
|
|
|
|
### Client errors (4xx)
|
|
|
|
| Code | Error | Description |
|
|
|------|-------|-------------|
|
|
| 400 | Bad Request | Invalid request syntax or parameters, or a quota that would be passed (`storage_limit_reached`) |
|
|
| 401 | Unauthorized | Missing or invalid authentication |
|
|
| 402 | Payment Required | Out of AI credits (`insufficient_credits`) |
|
|
| 403 | Forbidden | Authenticated but lacks permission, or the workspace's mailbox allowance is full (`mailbox_allowance_reached`) |
|
|
| 404 | Not Found | Resource doesn't exist |
|
|
| 409 | Conflict | Resource already exists |
|
|
| 422 | Unprocessable | Validation failed |
|
|
| 429 | Too Many Requests | Rate limit or AI usage cap exceeded (`rate_limit_exceeded`, `usage_cap_exceeded`) |
|
|
|
|
### Server errors (5xx)
|
|
|
|
| Code | Error | Description |
|
|
|------|-------|-------------|
|
|
| 500 | Internal Server Error | Unexpected server error |
|
|
| 501 | Not Implemented | Feature not available |
|
|
| 503 | Service Unavailable | Service temporarily down |
|
|
|
|
## Error details
|
|
|
|
### 400 Bad Request
|
|
|
|
Returned when the request cannot be processed due to invalid syntax.
|
|
|
|
**Common causes:**
|
|
- Invalid JSON in request body
|
|
- Missing required fields
|
|
- Invalid field types
|
|
- Values outside allowed ranges
|
|
|
|
**Example:**
|
|
|
|
```json
|
|
{
|
|
"error": "Bad Request",
|
|
"message": "invalid request body",
|
|
"code": "bad_request",
|
|
"request_id": "4bbbd1b2-8f86-47dd-8a7f-9476501ad20e"
|
|
}
|
|
```
|
|
|
|
**How to fix:**
|
|
- Check that your JSON is valid
|
|
- Verify all required fields are present
|
|
- Ensure field values match expected types
|
|
|
|
**Specific 400 codes:**
|
|
|
|
| `code` | Meaning |
|
|
|--------|---------|
|
|
| `invalid_lead_status` | `POST /contacts/search` or `POST /contacts/export` was given a `lead_status` that is not one of the documented values |
|
|
| `invalid_engagement` | `POST /contacts/search` or `POST /contacts/export` was given an `engagement` that is not one of the documented values |
|
|
| `lead_filter_requires_campaign` | `lead_status` or `engagement` was set without exactly one `campaign_ids` entry; both filters describe a contact inside one campaign |
|
|
| `unknown_verification_status` | A contact's `verification_status` is not a value any known verification service writes |
|
|
| `unknown_verification_provider` | A contact's `verification_provider` names a vocabulary the platform cannot read |
|
|
| `invalid_action` | `POST /contacts/verification` was given an `action` other than `verify`, `mark_deliverable` or `mark_undeliverable` |
|
|
| `no_contacts` | `POST /contacts/verification` selected no contacts: neither `contacts` nor a `campaign_id` with refused leads |
|
|
| `list_bounce_risk` | `POST /campaigns/:id/start` refused the launch on the list's projected bounce rate. Clean or verify the list, or repeat the request with `acknowledge_list_risk: true` |
|
|
| `leads_undeliverable` | `POST /campaigns/:id/start` found nothing to send because address verification refused every remaining lead; the campaign is parked at `paused_undeliverable` until they are re-verified or marked deliverable |
|
|
| `empty_step_body` | `POST /campaigns/:id/start` found an email step with nothing in either body, so it would send a blank message to every lead it reached. Write the step's body and start again |
|
|
| `no_leads` | `POST /campaigns/:id/start` on a campaign that has never had a lead, with `continuous` off. Add contacts, or set `continuous` so it starts empty and waits for them. A campaign whose leads have all finished is a different case: it starts and waits |
|
|
| `no_remaining_leads` | A platform-initiated restart of a campaign with nothing left to send and `continuous` off found nothing to do; the campaign is `completed` again. A start you request never answers this: it turns `continuous` on and waits |
|
|
| `too_many_tasks` | `PATCH /crm/tasks` or `DELETE /crm/tasks` was given more than `1000` ids in one request, or more than `50,000` exclusions. Split it into batches |
|
|
| `selection_too_large` | A `"all": true` bulk selection resolved to more than `50,000` rows. Narrow the filter and run it in parts; nothing was changed |
|
|
| `invalid_filter` | A task filter carried an id that is not one: `assigned_to`, `contact_id` and `deal_id` name records, and are matched against id columns. Sent by `POST /crm/tasks/search`, `POST /crm/tasks/summary`, and the `filters` of a `"all": true` bulk selection |
|
|
| `invalid_setting` | `PATCH /outreach/settings` (or a campaign's advanced settings) carried a value outside the documented vocabulary, for example a `reply_intent.crm_task_intents` entry that is not a reply intent |
|
|
| `no_organization` | The request needs a workspace and the caller has none selected. Every entitlement, limit and suppression rule is scoped to a workspace, so a write that would run unscoped is refused rather than run without those checks. API keys always carry their workspace; a dashboard session picks one at sign-in, so this normally means the session predates the workspace being chosen. Select a workspace and retry |
|
|
|
|
### 401 Unauthorized
|
|
|
|
Returned when authentication fails.
|
|
|
|
**Common causes:**
|
|
- Missing `Authorization` header
|
|
- Invalid API key format
|
|
- Expired API key
|
|
- Revoked API key
|
|
|
|
**Example:**
|
|
|
|
```json
|
|
{
|
|
"error": "Unauthorized",
|
|
"message": "Token not found.",
|
|
"code": "unauthorized",
|
|
"request_id": "4bbbd1b2-8f86-47dd-8a7f-9476501ad20e"
|
|
}
|
|
```
|
|
|
|
**How to fix:**
|
|
- Include the `Authorization: Bearer wmbly_...` header
|
|
- Verify your API key is correct
|
|
- Check if your key has expired or been revoked
|
|
- Generate a new key if necessary
|
|
|
|
Two `401` variants are not about API keys at all. `setup_token_invalid` is returned by the first-run claim endpoint when the setup link is invalid, already used or expired. Print a new one with `warmblyctl setup-link`, described in [first run](/development/first-run/).
|
|
|
|
`sso_wrong_browser` is returned by `POST /auth/sso/exchange` when the handoff code is collected without the binding secret that `POST /auth/<provider>/begin` handed the browser that started the sign-in. The handoff is deliberately non-transferable: a sign-in link that was forwarded, or opened in another browser, cannot sign the recipient in. Start the sign-in again in the browser you want to use.
|
|
|
|
### 402 Payment Required
|
|
|
|
Returned when an AI action is requested but the organization is out of credits. The response carries the stable code `insufficient_credits`.
|
|
|
|
```json
|
|
{
|
|
"error": "Payment Required",
|
|
"message": "You're out of AI credits. Add more to keep using the assistant.",
|
|
"code": "insufficient_credits",
|
|
"request_id": "req_..."
|
|
}
|
|
```
|
|
|
|
**How to fix:**
|
|
- Wait for the monthly allowance to reset, or buy a top-up pack (see [AI credits](/guides/ai-credits/))
|
|
- Related: a `429` with code `usage_cap_exceeded` means a short-term AI usage cap was hit; retry later
|
|
|
|
### 403 Forbidden
|
|
|
|
Returned when authenticated but lacking necessary permissions.
|
|
|
|
**Common causes:**
|
|
- API key lacks required permission
|
|
- Request IP not in allowlist
|
|
- Email account not in allowlist
|
|
- Organization access restricted
|
|
|
|
**Example:**
|
|
|
|
```json
|
|
{
|
|
"error": "Forbidden",
|
|
"message": "You don't have access to this feature.",
|
|
"code": "forbidden",
|
|
"request_id": "4bbbd1b2-8f86-47dd-8a7f-9476501ad20e"
|
|
}
|
|
```
|
|
|
|
**How to fix:**
|
|
- Check your API key's permissions
|
|
- Verify IP restrictions if configured
|
|
- Request additional permissions if needed
|
|
|
|
#### Registration and invitation refusals
|
|
|
|
Signup and invitation refusals carry their own `code`, so a client can branch on the specific condition instead of matching on text. They describe the deployment's policy and never say anything about whether a given address exists.
|
|
|
|
| `code` | Status | Meaning |
|
|
|--------|--------|---------|
|
|
| `registration_invite_only` | 403 | The server runs `DISABLE_REGISTRATION=invite_only`. Creating an account requires an invitation link, which carries the token that permits the signup |
|
|
| `registration_closed` | 403 | The server runs `DISABLE_REGISTRATION=true`. Signups are off and invitations do not override it |
|
|
| `invitation_invalid` | 403 | The invitation is expired, cancelled, already used, or was issued for a different email address |
|
|
| `setup_already_complete` | 403 | The first-run claim was attempted on an instance that already has an account |
|
|
|
|
```json
|
|
{
|
|
"error": "Forbidden",
|
|
"message": "This server is invite only. Ask an administrator to invite you, then open the link in the invitation to create your account.",
|
|
"code": "registration_invite_only",
|
|
"request_id": "4bbbd1b2-8f86-47dd-8a7f-9476501ad20e"
|
|
}
|
|
```
|
|
|
|
**How to fix:** on a self-hosted deployment these are configuration, not faults. See [accounts and access](/development/accounts-and-access/#registration-modes).
|
|
|
|
### 404 Not Found
|
|
|
|
Returned when the requested resource doesn't exist.
|
|
|
|
**Common causes:**
|
|
- Invalid resource ID
|
|
- Resource was deleted
|
|
- Resource belongs to different organization
|
|
- Typo in endpoint URL
|
|
|
|
**Example:**
|
|
|
|
```json
|
|
{
|
|
"error": "Not Found",
|
|
"message": "Resource not found.",
|
|
"code": "not_found",
|
|
"request_id": "4bbbd1b2-8f86-47dd-8a7f-9476501ad20e"
|
|
}
|
|
```
|
|
|
|
**How to fix:**
|
|
- Verify the resource ID is correct
|
|
- Check that the resource hasn't been deleted
|
|
- Ensure you're using the correct endpoint
|
|
|
|
### 409 Conflict
|
|
|
|
Returned when the request conflicts with existing data.
|
|
|
|
**Common causes:**
|
|
- Trying to create a resource that already exists
|
|
- Duplicate unique values
|
|
- Deleting something whose state does not allow it yet, such as an API key that can still authenticate
|
|
|
|
**Example:**
|
|
|
|
```json
|
|
{
|
|
"error": "Conflict",
|
|
"message": "resource already exists",
|
|
"code": "conflict",
|
|
"request_id": "4bbbd1b2-8f86-47dd-8a7f-9476501ad20e"
|
|
}
|
|
```
|
|
|
|
### 422 Unprocessable
|
|
|
|
Returned when validation fails on the request data.
|
|
|
|
**Common causes:**
|
|
- Invalid email format
|
|
- String exceeds maximum length
|
|
- Number outside valid range
|
|
- Invalid enum value
|
|
|
|
**Example:**
|
|
|
|
```json
|
|
{
|
|
"error": "Unprocessable",
|
|
"message": "validation failed",
|
|
"code": "unprocessable",
|
|
"request_id": "4bbbd1b2-8f86-47dd-8a7f-9476501ad20e"
|
|
}
|
|
```
|
|
|
|
### 500 Internal Server Error
|
|
|
|
Returned when an unexpected error occurs on the server.
|
|
|
|
**Example:**
|
|
|
|
```json
|
|
{
|
|
"error": "Internal Server Error",
|
|
"message": "Something went wrong.",
|
|
"code": "internal_error",
|
|
"request_id": "4bbbd1b2-8f86-47dd-8a7f-9476501ad20e"
|
|
}
|
|
```
|
|
|
|
**How to fix:**
|
|
- Retry the request after a short delay
|
|
- If persistent, contact support with request details
|
|
|
|
One `internal_error` variant is worth distinguishing. When an authentication endpoint cannot send its email, the message names that specifically rather than reporting a generic fault:
|
|
|
|
```json
|
|
{
|
|
"error": "Internal Server Error",
|
|
"message": "We couldn't send the email. If you administer this server, check the mail transport configuration.",
|
|
"code": "internal_error",
|
|
"request_id": "4bbbd1b2-8f86-47dd-8a7f-9476501ad20e"
|
|
}
|
|
```
|
|
|
|
On a self-hosted deployment that means `MAIL_TRANSPORT` and the `SMTP_` variables. See [self-hosting](/development/deployment-guide/).
|
|
|
|
### 503 Service Unavailable
|
|
|
|
Returned when the service is temporarily unavailable.
|
|
|
|
**Example:**
|
|
|
|
```json
|
|
{
|
|
"error": "Service Unavailable",
|
|
"message": "service unavailable",
|
|
"code": "service_unavailable",
|
|
"request_id": "4bbbd1b2-8f86-47dd-8a7f-9476501ad20e"
|
|
}
|
|
```
|
|
|
|
**How to fix:**
|
|
- Wait and retry with exponential backoff
|
|
- Check status page for incidents
|
|
|
|
#### `mailbox_provider_not_configured`
|
|
|
|
A `503` whose `code` is `mailbox_provider_not_configured` is not transient and retrying will not help. It means the deployment has no OAuth client for the mailbox provider the request asked for, which only happens on a self-hosted install.
|
|
|
|
```json
|
|
{
|
|
"error": "Service Unavailable",
|
|
"message": "Gmail is not configured on this deployment. Set BOX_GOOGLE_CLIENT_ID and BOX_GOOGLE_CLIENT_SECRET in your .env, then restart.",
|
|
"code": "mailbox_provider_not_configured",
|
|
"request_id": "4bbbd1b2-8f86-47dd-8a7f-9476501ad20e"
|
|
}
|
|
```
|
|
|
|
**How to fix:**
|
|
- Set `BOX_GOOGLE_CLIENT_ID` and `BOX_GOOGLE_CLIENT_SECRET`, or `BOX_OUTLOOK_CLIENT_ID` and `BOX_OUTLOOK_CLIENT_SECRET`, in the `.env` at your install root, then restart
|
|
- Or connect the mailbox over SMTP and IMAP instead, which needs no configuration
|
|
- Full walkthrough: [connect mailboxes](/development/deployment-guide/#connect-mailboxes)
|
|
|
|
#### `ai_not_configured`
|
|
|
|
A `503` whose `code` is `ai_not_configured` is not transient and retrying will not help. It comes from `POST /templates/analyze` and means the deployment has no AI provider set up at all. It is how a client tells "there is no AI here" apart from "the provider is having a bad minute", which returns the generic `service_unavailable` and is worth retrying.
|
|
|
|
```json
|
|
{
|
|
"error": "Service Unavailable",
|
|
"message": "AI analysis is not configured on this deployment.",
|
|
"code": "ai_not_configured",
|
|
"request_id": "4bbbd1b2-8f86-47dd-8a7f-9476501ad20e"
|
|
}
|
|
```
|
|
|
|
**How to fix:**
|
|
- Set `AI_PROVIDER` and `AI_API_KEY` in the `.env` at your install root, then restart. See the [configuration reference](/development/configuration/)
|
|
- Hide the AI affordance in your client rather than retrying: nothing about the request will make it succeed
|
|
|
|
#### `mailbox_allowance_reached`
|
|
|
|
A `403` whose `code` is `mailbox_allowance_reached` comes from every path that connects a mailbox: `POST /emails/onboarding/oauth/start`, `POST /emails/onboarding/oauth/finish`, `POST /emails/onboarding/smtp-imap`, and per row inside `POST /emails/onboarding/smtp-imap/bulk`. It is not a permission problem: the workspace holds its whole [mailbox allowance](/guides/mailboxes/#mailbox-allowance), which on a paid plan is one mailbox for every send a day the plan includes, and `10` on a free workspace. Nothing was connected.
|
|
|
|
```json
|
|
{
|
|
"error": "Forbidden",
|
|
"message": "This workspace holds 15000 of its 15000 mailboxes. Request an increase, or move to a plan with more daily sends.",
|
|
"code": "mailbox_allowance_reached",
|
|
"request_id": "4bbbd1b2-8f86-47dd-8a7f-9476501ad20e"
|
|
}
|
|
```
|
|
|
|
**How to fix:**
|
|
- Read `GET /emails/allowance` first: `remaining` says how many connects will succeed, and `pending_request` whether an increase is already asked for
|
|
- Submit a limit-increase request for `max_email_accounts` via `POST /organization/:orgId/limit-requests`, or move to a plan with more daily sends. An approved request raises the allowance immediately; retry the connect then
|
|
- Reconnecting an existing mailbox never returns this code
|
|
|
|
#### `storage_limit_reached`
|
|
|
|
A `400` whose `code` is `storage_limit_reached` comes from `POST /campaigns/:id/attachments`, from `POST /email-images`, and from a campaign duplicate that would copy attachments. The workspace's stored bytes, its attachments across every campaign plus its email image library, would pass the quota. The check and the write happen together under one per-workspace lock shared by both, so two uploads racing for the last of the quota cannot both get in. Nothing was stored.
|
|
|
|
```json
|
|
{
|
|
"error": "Bad Request",
|
|
"message": "Storage limit reached: 51190 MB of 51200 MB used, 12 MB to add. Remove attachments or images, or upgrade your plan.",
|
|
"code": "storage_limit_reached",
|
|
"request_id": "4bbbd1b2-8f86-47dd-8a7f-9476501ad20e"
|
|
}
|
|
```
|
|
|
|
**How to fix:**
|
|
- `GET /organization/current/limits` reports `storage.used_bytes` and `storage.limit_bytes`, and `storage.over_quota` when a plan change left the workspace above the quota. Existing attachments keep sending either way
|
|
- Delete attachments you no longer need (`DELETE /campaigns/:id/attachments/:attachmentId`) or images (`DELETE /email-images/:id`), or move to a paid plan for the larger quota
|
|
|
|
#### `mailbox_worker_unreachable`
|
|
|
|
A `503` whose `code` is `mailbox_worker_unreachable` comes from `DELETE /emails/{id}`. Disconnecting a mailbox has to reach the machine that syncs it before the record goes, because once the record is gone nothing can tell that machine to stop. When the instruction cannot be delivered, nothing is removed and the mailbox is left exactly as it was.
|
|
|
|
```json
|
|
{
|
|
"error": "Service Unavailable",
|
|
"message": "This mailbox could not be disconnected right now because the machine syncing it could not be reached. Nothing was removed, so try again in a moment.",
|
|
"code": "mailbox_worker_unreachable",
|
|
"request_id": "4bbbd1b2-8f86-47dd-8a7f-9476501ad20e"
|
|
}
|
|
```
|
|
|
|
**How to fix:**
|
|
- Retry the delete. It is safe to repeat: a mailbox that is already gone returns `404`, and one that is still there is untouched
|
|
|
|
#### `mailbox_send_as_unsupported`
|
|
|
|
A `400` whose `code` is `mailbox_send_as_unsupported` comes from `POST /emails/{id}/identity/refresh` and from a `PATCH /emails/{id}` that sets `send_as_email`. Only Gmail and Google Workspace mailboxes publish the addresses they are allowed to send as; an Outlook or SMTP/IMAP mailbox has no such list, so there is nothing to refresh and nothing to choose from.
|
|
|
|
```json
|
|
{
|
|
"error": "Bad Request",
|
|
"message": "This mailbox's provider does not expose send-as addresses. Only Gmail and Google Workspace mailboxes do.",
|
|
"code": "mailbox_send_as_unsupported",
|
|
"request_id": "9f0a6f21-2c7e-4f2d-9d0e-1f7b9a2c4e55"
|
|
}
|
|
```
|
|
|
|
**How to fix:**
|
|
- Read `GET /emails/{id}/identity` first. Its `supported` field is `false` for these mailboxes, and `identities` is empty
|
|
- Send from the mailbox's own address, which is what an empty `send_as_email` means
|
|
|
|
#### `mailbox_send_as_unknown`
|
|
|
|
A `400` whose `code` is `mailbox_send_as_unknown` comes from a `PATCH /emails/{id}` that sets `send_as_email` to an address the provider has not verified for that mailbox. The choice is refused here rather than at send time, where the provider's own refusal arrives days later against a campaign step and names nothing you could act on.
|
|
|
|
```json
|
|
{
|
|
"error": "Bad Request",
|
|
"message": "That address is not one your provider has verified this mailbox to send as. Refresh the list, or add and verify the address in your provider first.",
|
|
"code": "mailbox_send_as_unknown",
|
|
"request_id": "b71c3f88-0b3e-4a41-9a52-2f4f1fd0c0aa"
|
|
}
|
|
```
|
|
|
|
**How to fix:**
|
|
- Add the alias in Gmail (Settings, Accounts, "Send mail as") and finish its verification
|
|
- Call `POST /emails/{id}/identity/refresh` so Warmbly re-reads the list, then set `send_as_email` to an address whose `verified` is `true`
|
|
|
|
#### `mailbox_signature_too_large`
|
|
|
|
A `400` whose `code` is `mailbox_signature_too_large` comes from `POST /emails/{id}/identity/refresh` with `import_signature` set. The provider's signature is larger than Warmbly stores (`20000` characters of HTML), and it is refused rather than truncated: half a signature is worse than none. The send-as list is not stored either, so the call changes nothing.
|
|
|
|
```json
|
|
{
|
|
"error": "Bad Request",
|
|
"message": "The signature on this mailbox is larger than Warmbly stores (20000 characters). Shorten it in your provider and import it again.",
|
|
"code": "mailbox_signature_too_large",
|
|
"request_id": "2d5e9a13-7c41-4f9b-bb17-a0f1d9c6e332"
|
|
}
|
|
```
|
|
|
|
**How to fix:**
|
|
- Shorten the signature in Gmail, usually by linking an image rather than embedding it, and import again
|
|
- Or write the signature in Warmbly directly with `PATCH /emails/{id}`
|
|
|
|
## Error handling best practices
|
|
|
|
### Implement retry logic
|
|
|
|
For transient errors (5xx, 429), implement exponential backoff:
|
|
|
|
```javascript
|
|
async function requestWithRetry(url, options, maxRetries = 3) {
|
|
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
try {
|
|
const response = await fetch(url, options);
|
|
|
|
if (response.ok) {
|
|
return response.json();
|
|
}
|
|
|
|
// Don't retry client errors (4xx) except rate limits
|
|
if (response.status >= 400 && response.status < 500 && response.status !== 429) {
|
|
throw new Error(`Client error: ${response.status}`);
|
|
}
|
|
|
|
// Retry server errors and rate limits
|
|
if (attempt < maxRetries - 1) {
|
|
const delay = Math.pow(2, attempt) * 1000;
|
|
await new Promise(resolve => setTimeout(resolve, delay));
|
|
continue;
|
|
}
|
|
} catch (error) {
|
|
if (attempt === maxRetries - 1) throw error;
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
### Parse error responses
|
|
|
|
Always parse and handle error responses:
|
|
|
|
```javascript
|
|
async function apiRequest(url, options) {
|
|
const response = await fetch(url, options);
|
|
|
|
if (!response.ok) {
|
|
const error = await response.json();
|
|
throw new ApiError(response.status, error.error, error.message);
|
|
}
|
|
|
|
return response.json();
|
|
}
|
|
|
|
class ApiError extends Error {
|
|
constructor(status, type, message) {
|
|
super(message);
|
|
this.status = status;
|
|
this.type = type;
|
|
}
|
|
}
|
|
```
|
|
|
|
## Rate limiting
|
|
|
|
When you exceed rate limits, you'll receive:
|
|
|
|
```
|
|
HTTP/1.1 429 Too Many Requests
|
|
Retry-After: 60
|
|
```
|
|
|
|
```json
|
|
{
|
|
"error": "Too Many Requests",
|
|
"message": "Rate limit exceeded. Please retry after 60 seconds.",
|
|
"code": "rate_limit_exceeded",
|
|
"request_id": "4bbbd1b2-8f86-47dd-8a7f-9476501ad20e"
|
|
}
|
|
```
|
|
|
|
Use the `Retry-After` header to determine when to retry.
|
|
|
|
## See also
|
|
|
|
- [Authentication](/api/authentication/)
|
|
- [Endpoints](/api/endpoints/)
|