{"openapi":"3.1.0","info":{"title":"unLocked CRM API (Public Subset)","version":"1.0.0","description":"Complete API surface of the unLocked CRM platform.\n\n**Two tiers:**\n1. **Public API** (`/public-api`) — stable, documented REST API for external integrations. API-key or OAuth Bearer auth, scoped permissions, 100 req/min rate limit. The `resource` query parameter selects the collection; the HTTP method selects the operation.\n2. **Edge Functions** (everything else) — internal serverless endpoints that power the app UI, AI features, telephony, quoting, automations, and third-party webhooks. These are documented for completeness; request shapes are function-specific and not covered by the public API stability guarantee.\n\nAll endpoints are Supabase Edge Functions invoked over HTTPS with JSON bodies. `OPTIONS` preflight is supported on every function.\n\nSlim public subset: internal/system operations removed. Full spec: https://unlockedcrm.ai/openapi.json","contact":{"name":"unLocked Support","email":"support@unlockedcrm.ai"},"x-rate-limit":{"description":"Default 120 requests/minute per API key (burst 20/second). Exceeding it returns 429 with a Retry-After header.","requestsPerMinute":120,"burstPerSecond":20,"retryAfterHeader":"Retry-After"}},"servers":[{"url":"https://gzigyepfasiumngxilai.supabase.co/functions/v1","description":"Production"}],"security":[{"OAuth2":["read:contacts","write:contacts","read:leads","write:leads","read:policies","write:policies","read:quotes","write:quotes","read:commissions","write:commissions","read:tasks","write:tasks","read:appointments","write:appointments","read:activities","write:activities","read:communications","write:communications","read:agent_ai","write:agent_ai"]},{"ApiKeyAuth":[]},{"BearerAuth":[]}],"components":{"securitySchemes":{"ApiKeyAuth":{"type":"apiKey","in":"header","name":"x-api-key","description":"Public API key (scoped, rate-limited). Created in Settings → API. Keys are issued with an explicit subset of the named scopes listed in x-scopes (same scope names as the OAuth2 scheme).","x-scopes":{"read:contacts":"Read contact records","write:contacts":"Create and update contact records","read:leads":"Read leads and pipeline records","write:leads":"Create and update leads","read:policies":"Read policy records, renewals, and lapse status","write:policies":"Create and update policy records","read:quotes":"Read quotes and comparison results","write:quotes":"Create and update quotes","read:commissions":"Read commission statements and reconciliation data","write:commissions":"Create and update commission records","read:tasks":"Read tasks and reminders","write:tasks":"Create and update tasks","read:appointments":"Read calendar appointments","write:appointments":"Create and update appointments","read:activities":"Read activity and timeline history","write:activities":"Write activity and timeline entries","read:communications":"Read calls, SMS, and email history","write:communications":"Send SMS, email, and place calls","read:agent_ai":"Read AI agent configuration and run history","write:agent_ai":"Trigger AI agent runs and update configuration"}},"BearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"Supabase user JWT (Authorization: Bearer <token>) — the session token of a signed-in app user."},"ToolSecretAuth":{"type":"apiKey","in":"header","name":"X-Tool-Secret","description":"Shared server-to-server secret for AI agent tool webhooks."},"WebhookSignature":{"type":"apiKey","in":"header","name":"Stripe-Signature / X-Twilio-Signature / provider secret","description":"Provider-signed webhook verification (varies by provider)."},"OAuth2":{"type":"oauth2","description":"OAuth 2.0 authorization code flow with PKCE against the unLocked CRM authorization server. Discovery: https://unlockedcrm.ai/.well-known/oauth-authorization-server. Protected-resource metadata (RFC 9728): https://unlockedcrm.ai/.well-known/oauth-protected-resource. Request least-privilege scopes.","flows":{"authorizationCode":{"authorizationUrl":"https://mcp.unlockedcrm.ai/oauth?action=authorize","tokenUrl":"https://mcp.unlockedcrm.ai/oauth?action=token","refreshUrl":"https://mcp.unlockedcrm.ai/oauth?action=token","scopes":{"read:contacts":"Read contact records","write:contacts":"Create and update contact records","read:leads":"Read leads and pipeline records","write:leads":"Create and update leads","read:policies":"Read policy records, renewals, and lapse status","write:policies":"Create and update policy records","read:quotes":"Read quotes and comparison results","write:quotes":"Create and update quotes","read:commissions":"Read commission statements and reconciliation data","write:commissions":"Create and update commission records","read:tasks":"Read tasks and reminders","write:tasks":"Create and update tasks","read:appointments":"Read calendar appointments","write:appointments":"Create and update appointments","read:activities":"Read activity and timeline history","write:activities":"Write activity and timeline entries","read:communications":"Read calls, SMS, and email history","write:communications":"Send SMS, email, and place calls","read:agent_ai":"Read AI agent configuration and run history","write:agent_ai":"Trigger AI agent runs and update configuration"}}}}},"schemas":{"Lead":{"type":"object","description":"Standard lead fields are written to the leads table. Unknown top-level keys (or keys nested under `custom_fields`) are resolved against the workspace's custom_fields definitions. Common Zapier/GHL aliases are auto-rewritten (see `x-lead-field-aliases`).","additionalProperties":true,"properties":{"first_name":{"type":"string"},"last_name":{"type":"string"},"name":{"type":"string","description":"Auto-split into first_name/last_name when those are absent."},"email":{"type":"string","format":"email"},"phone":{"type":"string"},"status":{"type":"string"},"source":{"type":"string"},"custom_fields":{"type":"object","additionalProperties":true,"example":{"income":"85000","preferred_contact_time":"Evenings"}}},"example":{"first_name":"Jane","last_name":"Doe","email":"jane@example.com","income":"85000","custom_fields":{"preferred_contact_time":"Evenings"}}},"LeadWriteResult":{"type":"object","properties":{"data":{"type":"object","description":"The created/updated lead row."},"matched_custom_fields":{"type":"array","items":{"type":"string"}},"unknown_fields":{"type":"array","items":{"type":"string"}},"custom_field_errors":{"type":"array","items":{"type":"object","properties":{"field":{"type":"string"},"error":{"type":"string"}}}}}},"BulkRequest":{"type":"object","description":"Envelope for `action=bulk`. Up to 1,000 records per request.","properties":{"mode":{"type":"string","enum":["create","update","upsert"],"default":"create"},"records":{"type":"array","items":{"type":"object","additionalProperties":true},"maxItems":1000}},"required":["records"]},"Error":{"type":"object","description":"Every non-2xx response from the unLocked CRM API is `application/json` with this envelope. HTML error pages are never returned to API clients.","required":["error"],"properties":{"error":{"type":"object","required":["code","message","status","retryable"],"properties":{"code":{"type":"string","description":"Stable machine-readable error code.","enum":["invalid_request","validation_failed","unauthenticated","permission_denied","not_found","conflict","rate_limited","upstream_error","internal_error"]},"message":{"type":"string","description":"Human-readable description of what went wrong."},"status":{"type":"integer","description":"HTTP status code, repeated in the body for agents that only read bodies."},"retryable":{"type":"boolean","description":"True when re-sending the same request may succeed after a delay."},"resolution":{"type":"string","description":"Actionable hint describing how to fix or recover from the error."},"required_scope":{"type":"string","description":"On 403, the OAuth scope the caller is missing."},"retry_after_seconds":{"type":"integer","description":"On 429/503, how long to wait before retrying."},"details":{"type":"array","description":"Field-level problems, when applicable.","items":{"type":"object","properties":{"field":{"type":"string"},"issue":{"type":"string"}}}},"request_id":{"type":"string","description":"Correlation id to quote in support requests."},"docs_url":{"type":"string","format":"uri","description":"Link to documentation for this error code."}}}}},"GenericResponse":{"type":"object","description":"Standard JSON response envelope. `success` indicates whether the operation completed; `data` carries the operation-specific payload.","properties":{"success":{"type":"boolean","description":"True when the operation succeeded."},"data":{"description":"Operation-specific payload. Shape depends on the endpoint."},"message":{"type":"string","description":"Human-readable status message."}},"required":["success"]}},"responses":{"BadRequest":{"description":"Request was malformed or referenced an unknown resource.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"invalid_request":{"summary":"Request was malformed or referenced an unknown resource.","value":{"error":{"code":"invalid_request","message":"Request was malformed or referenced an unknown resource.","status":400,"retryable":false,"resolution":"Check the `resource` query parameter and required body fields, then retry.","request_id":"req_01J9ZC7Q2X5T","docs_url":"https://unlockedcrm.ai/api#errors","details":[{"field":"resource","issue":"unknown resource value"}]}}}}}}},"Unauthorized":{"description":"Missing or invalid credentials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"unauthenticated":{"summary":"Missing or invalid credentials.","value":{"error":{"code":"unauthenticated","message":"Missing or invalid credentials.","status":401,"retryable":false,"resolution":"Send a valid `x-api-key` header or `Authorization: Bearer <jwt>` and retry.","request_id":"req_01J9ZC7Q2X5T","docs_url":"https://unlockedcrm.ai/api#errors"}}}}}}},"Forbidden":{"description":"Permission denied for this resource or action.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"permission_denied":{"summary":"Permission denied for this resource or action.","value":{"error":{"code":"permission_denied","message":"Permission denied for this resource or action.","status":403,"retryable":false,"resolution":"Request the scope named in `error.required_scope` (see the scopes table) and retry.","request_id":"req_01J9ZC7Q2X5T","docs_url":"https://unlockedcrm.ai/api#errors","required_scope":"leads:write"}}}}}}},"NotFound":{"description":"The requested record or endpoint does not exist.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"not_found":{"summary":"The requested record or endpoint does not exist.","value":{"error":{"code":"not_found","message":"The requested record or endpoint does not exist.","status":404,"retryable":false,"resolution":"Verify the `id` and workspace, then retry. Do not retry with the same id.","request_id":"req_01J9ZC7Q2X5T","docs_url":"https://unlockedcrm.ai/api#errors"}}}}}}},"Conflict":{"description":"The write conflicts with existing state.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"conflict":{"summary":"The write conflicts with existing state.","value":{"error":{"code":"conflict","message":"The write conflicts with existing state.","status":409,"retryable":false,"resolution":"Fetch the current record, merge, and retry once.","request_id":"req_01J9ZC7Q2X5T","docs_url":"https://unlockedcrm.ai/api#errors"}}}}}}},"UnprocessableEntity":{"description":"One or more field values failed validation.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"validation_failed":{"summary":"One or more field values failed validation.","value":{"error":{"code":"validation_failed","message":"One or more field values failed validation.","status":422,"retryable":false,"resolution":"Read `error.details[]` for the offending fields and correct them before retrying.","request_id":"req_01J9ZC7Q2X5T","docs_url":"https://unlockedcrm.ai/api#errors","details":[{"field":"email","issue":"must be a valid email address"}]}}}}}}},"RateLimited":{"description":"Rate limit exceeded (100 requests/minute).","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"rate_limited":{"summary":"Rate limit exceeded (100 requests/minute).","value":{"error":{"code":"rate_limited","message":"Rate limit exceeded (100 requests/minute).","status":429,"retryable":true,"resolution":"Wait the number of seconds in the `Retry-After` header, then retry with backoff.","request_id":"req_01J9ZC7Q2X5T","docs_url":"https://unlockedcrm.ai/api#errors","retry_after_seconds":30}}}}}}},"ServerError":{"description":"Unexpected server error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"internal_error":{"summary":"Unexpected server error.","value":{"error":{"code":"internal_error","message":"Unexpected server error.","status":500,"retryable":true,"resolution":"Retryable. Retry with exponential backoff and include `error.request_id` in any support request.","request_id":"req_01J9ZC7Q2X5T","docs_url":"https://unlockedcrm.ai/api#errors"}}}}}}},"BadGateway":{"description":"A downstream carrier or provider API failed.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"},"examples":{"upstream_error":{"summary":"A downstream carrier or provider API failed.","value":{"error":{"code":"upstream_error","message":"A downstream carrier or provider API failed.","status":502,"retryable":true,"resolution":"Retryable. Retry with exponential backoff; if it persists, contact info@unlockedcrm.ai.","request_id":"req_01J9ZC7Q2X5T","docs_url":"https://unlockedcrm.ai/api#errors"}}}}}}}}},"tags":[{"name":"Public API","description":"Stable external REST API — the only surface covered by the stability guarantee."},{"name":"AI & Agents","description":"Internal edge functions (documented for completeness; no stability guarantee)."},{"name":"Admin & Provisioning","description":"Internal edge functions (documented for completeness; no stability guarantee)."},{"name":"Automation & Scheduled Jobs","description":"Internal edge functions (documented for completeness; no stability guarantee)."},{"name":"Billing & Payments","description":"Internal edge functions (documented for completeness; no stability guarantee)."},{"name":"Calendar & Booking","description":"Internal edge functions (documented for completeness; no stability guarantee)."},{"name":"Core CRM","description":"Internal edge functions (documented for completeness; no stability guarantee)."},{"name":"Email & Notifications","description":"Internal edge functions (documented for completeness; no stability guarantee)."},{"name":"Integrations & Webhooks","description":"Internal edge functions (documented for completeness; no stability guarantee)."},{"name":"Quoting & Enrollment","description":"Internal edge functions (documented for completeness; no stability guarantee)."},{"name":"Telephony & Messaging","description":"Internal edge functions (documented for completeness; no stability guarantee)."}],"paths":{"/public-api":{"get":{"tags":["Public API"],"summary":"List a collection, or fetch one record","description":"Omit `id` to list the collection, or pass `id` to fetch a single record. `resource=health` needs no credentials; `resource=whoami` reports the workspace and permissions the credentials resolve to.","parameters":[{"name":"resource","in":"query","required":true,"description":"Which collection the request addresses.","schema":{"type":"string","enum":["leads","clients","activities","tasks","policies","appointments","communications","webhooks","pipelines","pipeline_stages","opportunities","workflows","email_campaigns","commissions","aca_leads","agent_ai_call_logs","agent_ai_scripts","agent_ai_campaigns","quotes","tags","call_recordings","call_disposition_logs","agent_presence","inbound_campaigns","inbound_vendors","health","whoami"]},"example":"leads"},{"name":"id","in":"query","description":"Record id. Omit to list; required for single-record fetch.","schema":{"type":"string","format":"uuid"}},{"name":"limit","in":"query","schema":{"type":"integer","maximum":100,"default":50}},{"name":"offset","in":"query","schema":{"type":"integer","default":0}},{"name":"sort","in":"query","description":"field:direction","schema":{"type":"string","example":"created_at:desc"}},{"name":"fields","in":"query","description":"Comma-separated columns to return.","schema":{"type":"string"}},{"name":"search","in":"query","schema":{"type":"string"}},{"name":"status","in":"query","schema":{"type":"string"}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}},"x-error-format":"application/json; envelope=error","operationId":"getPublicApi"},"post":{"tags":["Public API"],"summary":"Create records","description":"Creates one record from the request body. Pass `action=bulk` with a `records` array to create up to 1,000 at once. For `resource=leads` the body follows the Lead schema — unknown keys are resolved against the workspace's custom fields.","parameters":[{"name":"resource","in":"query","required":true,"schema":{"type":"string"},"example":"leads"},{"name":"action","in":"query","description":"Set to `bulk` to submit a records array.","schema":{"type":"string","enum":["bulk"]}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Lead"},{"$ref":"#/components/schemas/BulkRequest"},{"type":"object","additionalProperties":true,"description":"Record body for any non-lead resource."}]}}}},"responses":{"200":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeadWriteResult"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}},"x-error-format":"application/json; envelope=error","operationId":"postPublicApi"},"put":{"tags":["Public API"],"summary":"Update a record","parameters":[{"name":"resource","in":"query","required":true,"schema":{"type":"string"}},{"name":"id","in":"query","required":true,"schema":{"type":"string","format":"uuid"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"oneOf":[{"$ref":"#/components/schemas/Lead"},{"type":"object","additionalProperties":true,"description":"Record body for any non-lead resource."}]}}}},"responses":{"200":{"description":"Updated","content":{"application/json":{"schema":{"$ref":"#/components/schemas/LeadWriteResult"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}},"x-error-format":"application/json; envelope=error","operationId":"putPublicApi","description":"Update a record"},"delete":{"tags":["Public API"],"summary":"Delete a record","parameters":[{"name":"resource","in":"query","required":true,"schema":{"type":"string"}},{"name":"id","in":"query","required":true,"schema":{"type":"string","format":"uuid"}}],"responses":{"200":{"description":"Deleted","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenericResponse"}}}},"401":{"$ref":"#/components/responses/Unauthorized"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}},"x-error-format":"application/json; envelope=error","operationId":"deletePublicApi","description":"Delete a record"}},"/openapi-spec":{"get":{"tags":["Public API"],"summary":"OpenAPI 3.1 specification for the Public API","description":"Machine-readable spec for the public REST API. Anonymous. `?format=yaml` is reserved.","security":[],"responses":{"200":{"description":"OpenAPI JSON document","content":{"application/json":{"schema":{"type":"object"}}}},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}},"x-error-format":"application/json; envelope=error","operationId":"getOpenapiSpec"}},"/mcp-server":{"post":{"tags":["Core CRM"],"summary":"MCP (Model Context Protocol) server wrapper around the unLocked Public API.","description":"MCP (Model Context Protocol) server wrapper around the unLocked Public API. Lets AI agents (ChatGPT, Claude, Cursor) discover and call CRM tools natively.\n\n**Auth:** CRM API key via x-api-key header OR OAuth 2.0 access token via Authorization: Bearer (MCP clients).","security":[{"ApiKeyAuth":[]}],"x-internal":false,"x-function-name":"mcp-server","responses":{"200":{"description":"Success (JSON body; shape is function-specific)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GenericResponse"}}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthorized"},"429":{"$ref":"#/components/responses/RateLimited"},"500":{"$ref":"#/components/responses/ServerError"}},"requestBody":{"required":false,"content":{"application/json":{"schema":{"type":"object","additionalProperties":true,"description":"Function-specific payload. This is an internal endpoint — see api.md for the auth contract; request shapes are defined by the app that calls it."}}}},"x-error-format":"application/json; envelope=error","operationId":"postMcpServer"}}},"x-lead-field-aliases":{"income":"household_income","annual_income":"household_income","dob":"date_of_birth","zipcode":"zip_code","postal_code":"zip_code","phone_number":"phone","mobile":"phone","company":"employer","job_title":"occupation"},"x-webhook-events":["lead.created","lead.updated","lead.deleted","lead.status_changed","client.created","client.updated","task.created","task.completed","policy.created","policy.renewed","appointment.created","appointment.cancelled","activity.created","opportunity.created","opportunity.updated","opportunity.stage_changed","workflow.activated","workflow.deactivated","email_campaign.sent","email_campaign.completed","commission.created","commission.updated","sms.sent","sms.received","sms.delivered","sms.failed","quote.saved"],"x-cron-functions":["agent-ai-campaign-processor","agent-ai-elevenlabs-reconcile-stuck","agent-builtin-scheduler","agent-lead-conversion","agent-user-executor","ai-tool-drift-check","calendly-poll-sync","capability-scan","csg-eapp-sync","lead-cap-billing-trigger","meta-refresh-tokens","meta-retry-leads","notify-agency-new-signup","phone-system-alerts","process-birthday-reminders","process-booking-nudges","process-campaigns","process-custom-date-reminders","process-email-campaign-retries","process-enrollment-period","process-medicare-eligibility","process-medicare-pending-eligible","process-policy-renewal","process-scheduled-nudges","process-scheduled-opportunities","process-scheduled-quote-sends","process-single-email-queue","recover-missed-onboarding","send-affiliate-signup-notification","system-threshold-monitor","trigger-workflow","webhook-dispatcher","webhook-emit","webhook-retry-worker","workflow-queue-listener"],"x-function-count":538,"x-scopes":{"read:contacts":"Read contact records","write:contacts":"Create and update contact records","read:leads":"Read leads and pipeline records","write:leads":"Create and update leads","read:policies":"Read policy records, renewals, and lapse status","write:policies":"Create and update policy records","read:quotes":"Read quotes and comparison results","write:quotes":"Create and update quotes","read:commissions":"Read commission statements and reconciliation data","write:commissions":"Create and update commission records","read:tasks":"Read tasks and reminders","write:tasks":"Create and update tasks","read:appointments":"Read calendar appointments","write:appointments":"Create and update appointments","read:activities":"Read activity and timeline history","write:activities":"Write activity and timeline entries","read:communications":"Read calls, SMS, and email history","write:communications":"Send SMS, email, and place calls","read:agent_ai":"Read AI agent configuration and run history","write:agent_ai":"Trigger AI agent runs and update configuration"},"x-error-format":{"media_type":"application/json","envelope":"{\"error\":{\"code\",\"message\",\"status\",\"retryable\",\"resolution\"}}","html_errors":false,"description":"All non-2xx API responses are JSON with a stable `error.code`, human `error.message`, and an actionable `error.resolution`. Agents should branch on `error.code` and honor `error.retryable` / `Retry-After`.","codes":[{"code":"invalid_request","http_status":400,"meaning":"Request was malformed, missing a required parameter, or referenced an unknown resource.","resolution":"Check the `resource` query parameter and required body fields, then retry.","retryable":false},{"code":"validation_failed","http_status":422,"meaning":"One or more field values failed validation.","resolution":"Read `error.details[]` for the offending fields and correct them before retrying.","retryable":false},{"code":"unauthenticated","http_status":401,"meaning":"Missing, malformed, or expired credentials.","resolution":"Send a valid `x-api-key` header or `Authorization: Bearer <jwt>` and retry.","retryable":false},{"code":"permission_denied","http_status":403,"meaning":"The credentials are valid but lack the scope required for this operation.","resolution":"Request the scope named in `error.required_scope` (see the scopes table) and retry.","retryable":false},{"code":"not_found","http_status":404,"meaning":"The record or endpoint does not exist, or is outside the caller's workspace.","resolution":"Verify the `id` and workspace, then retry. Do not retry with the same id.","retryable":false},{"code":"conflict","http_status":409,"meaning":"The write conflicts with existing state (duplicate key, concurrent update).","resolution":"Fetch the current record, merge, and retry once.","retryable":false},{"code":"rate_limited","http_status":429,"meaning":"Rate limit exceeded (100 requests/minute per key).","resolution":"Wait the number of seconds in the `Retry-After` header, then retry with backoff.","retryable":true},{"code":"upstream_error","http_status":502,"meaning":"A downstream carrier or provider API failed.","resolution":"Retryable. Retry with exponential backoff; if it persists, contact info@unlockedcrm.ai.","retryable":true},{"code":"internal_error","http_status":500,"meaning":"Unexpected server error.","resolution":"Retryable. Retry with exponential backoff and include `error.request_id` in any support request.","retryable":true}]}}