From a919dea79f71259ea30935f889141fb74afbab46 Mon Sep 17 00:00:00 2001 From: Rajeh Taher Date: Sun, 27 Sep 2026 00:53:20 +0200 Subject: [PATCH 1/9] feat(sdk): reconcile native release and campaign contracts --- contracts/README.md | 41 +- contracts/coverage.json | 67 +- contracts/openapi.messaging.json | 4231 ++++++++--------- contracts/openapi.platform.json | 222 +- contracts/source.json | 12 +- contracts/testing-events.json | 24 +- packages/typescript/src/index.ts | 5 + .../typescript/src/messaging/campaigns.ts | 32 + .../src/messaging/testing-configuration.ts | 7 - packages/typescript/src/messaging/types.ts | 16 + packages/typescript/src/platform/campaigns.ts | 14 + .../src/platform/developer-resources.ts | 6 +- .../src/platform/developer-types.ts | 5 + packages/typescript/src/platform/types.ts | 5 + packages/typescript/test/campaigns.test.ts | 42 + .../test/coverage-reconciliation.test.ts | 2 +- packages/typescript/test/coverage.test.ts | 6 +- .../test/organization-webhook-test.test.ts | 42 + .../test/platform-campaign-create.test.ts | 31 + .../typescript/test/testing-events.test.ts | 2 +- 20 files changed, 2525 insertions(+), 2287 deletions(-) create mode 100644 packages/typescript/test/organization-webhook-test.test.ts diff --git a/contracts/README.md b/contracts/README.md index e9e5aa44..dcb6bb63 100644 --- a/contracts/README.md +++ b/contracts/README.md @@ -29,27 +29,34 @@ operations. The six Console counterparts are excluded. Message content and `call.permission_changed` are typed in server and browser packages where applicable. -`testing-events.json` records the four test-event schemas from API commit -`9c876c16c60b74370d934e1275f23ef6096bee12`, including the source path and file -hash. The TypeScript test-event catalog and override types use that revision. +`testing-events.json` records the four test-event schemas from the same API +source commit as the full snapshots, including the source path and file hash. +The TypeScript test-event catalog and override types use that revision. Local schema references are rebased to this supplement's `schemas` root. The fixture contract test compares the exported catalog against this snapshot. -It adds `session.restriction_updated`, its boolean `restrictionActive` override, -and `call_restricted` to `callEndReason`. The full snapshots and coverage ledger -below use the merged API revision. CLI consumers require a +It includes `session.restriction_updated`, its boolean `restrictionActive` +override, and `call_restricted` in `callEndReason`. The retired BanSafe test +fixtures are absent. CLI consumers require a published SDK package before updating their pinned dependency. ## Full snapshots The snapshots are byte-identical copies of the Messaging and Platform OpenAPI -files at merged `polymorfa/polymorfa` API `dev` commit -`5cba4237fcec1ef776e8d3727c7ecc4d8f0207a3`. This revision integrates -Hybrid Link and the `campaign.launched`, `campaign.resumed`, and -`campaign.stopped` webhook schemas from API feature source -`3558c289ec35aba45a498a8794a94b4919365899`. The three events change no -operation fingerprint or SDK method mapping. The snapshots, ledger, and revision -tests have been reconciled. `source.json` records the source paths and SHA-256 -hashes. SDK package publication remains separate. +files at `polymorfa/polymorfa` PR #388 source commit `2f24c1d62` (coordinated +until that PR merges). The response to feature evaluation now includes a nullable +`experimentRevision`; both evaluation routes remain excluded from SDK methods +because they require a host runtime or live dashboard identity. The full refresh +also reconciles the Campaigns update and reschedule routes. `source.json` records +the exact source paths and SHA-256 hashes. SDK package publication remains +separate. + +`MessagingClient.campaigns.update` covers the new draft update route, while +`MessagingClient.campaigns.reschedule` and `Client.campaigns.reschedule` cover +moving a launched campaign that is waiting to start. The Messaging update is +sent once after an uncertain response because its route has no replay key; +reschedule uses the API's idempotency key. The Platform reschedule request names +`projectId` for an organization credential. The other refreshed operation +fingerprints were checked against their existing method paths and types. The merged Campaigns failed-state follow-up updates the `campaign.failed` reason example and the public archive/delete `409` descriptions. It changes no @@ -102,7 +109,7 @@ payment-required error class. The webhook catalog also adds the restriction payload, requires `code` on `session.logged_out`, narrows its reason enum, and narrows the previous BanSafe health band; SDK types follow those schemas. -The latest schema refresh changes only `POST /platform/campaigns`: its required +An earlier schema refresh changes only `POST /platform/campaigns`: its required body is now `CreatePlatformCampaignRequest`, with required `name`, named optional fields, and no additional top-level properties. The SDK requires `projectId` because its Platform campaign resource belongs to organization clients. The @@ -160,12 +167,12 @@ too, with their payload types. | Status | Operations | | ------------------- | ---------: | -| Covered | 379 | +| Covered | 382 | | Missing | 0 | | Excluded | 130 | | Partial | 0 | | Changed fingerprint | 0 | -| Total | 509 | +| Total | 512 | This revision adds test event triggering (`POST /messaging/testing/{projectId}/events`) and fixture listing diff --git a/contracts/coverage.json b/contracts/coverage.json index d86e228b..6f4b1ecb 100644 --- a/contracts/coverage.json +++ b/contracts/coverage.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "sourceCommit": "5cba4237fcec1ef776e8d3727c7ecc4d8f0207a3", + "sourceCommit": "2f24c1d62", "excludedContracts": [ { "family": "graph", @@ -772,7 +772,7 @@ "method": "GET", "path": "/messaging/projects/{projectSlug}/campaigns/{id}", "operationId": "getCampaign", - "fingerprint": "b3c5430236e9e7539866777d4a2804a5b622ef023659ee7200bbceddf73958b3", + "fingerprint": "df0afb657e91437629b726fc004b1c769343ee277d1dcbe235955d6dc1432d9b", "typescript": { "status": "covered", "method": "MessagingClient.campaigns.retrieve" @@ -783,7 +783,7 @@ "method": "GET", "path": "/messaging/projects/{projectSlug}/campaigns/{id}/analytics", "operationId": "getCampaignAnalytics", - "fingerprint": "954b6bf88195055987a02b453307322d6ac1949e82fe4fb9f0a15fcd03929ed6", + "fingerprint": "7060015a6c91559b6ae6a0b7e8212219ddaf4c00675e0f9c3fbb7c3ceccdb00b", "typescript": { "status": "covered", "method": "MessagingClient.campaigns.analytics" @@ -860,7 +860,7 @@ "method": "GET", "path": "/messaging/testing/{projectId}/events/fixtures", "operationId": "listTestEventFixtures", - "fingerprint": "6dc56807cfc48718a9e5da98ce3c3649694f718d4643b333b75e7341d82943b2", + "fingerprint": "05b7145174ef35215dd9b6f51b83927279d12a1281e4f142f882e13c27fb5361", "typescript": { "status": "covered", "method": "MessagingClient.testing.listEventFixtures" @@ -978,6 +978,17 @@ "method": "MessagingClient.business.updateProfile" } }, + { + "family": "messaging", + "method": "PATCH", + "path": "/messaging/projects/{projectSlug}/campaigns/{id}", + "operationId": "updateProjectCampaign", + "fingerprint": "fe6366542a59aaf35a743b1d4fc2819fc0c192aeca7db53e41a6370de886f3fa", + "typescript": { + "status": "covered", + "method": "MessagingClient.campaigns.update" + } + }, { "family": "messaging", "method": "PATCH", @@ -1434,7 +1445,7 @@ "method": "POST", "path": "/messaging/features/evaluate", "operationId": "evaluateFeatures", - "fingerprint": "552c29c31cd255e674168226ef182a837b657036bc706b8ccb8e1a90bd530d22", + "fingerprint": "4fcfc98dd98689726893fbb4e3afbb625a1abc1e0816ed44cdc063434db8f389", "typescript": { "status": "excluded", "reason": "Host-runtime feature evaluation is not a customer messaging operation in this SDK." @@ -1456,7 +1467,7 @@ "method": "POST", "path": "/messaging/projects/{projectSlug}/campaigns", "operationId": "createCampaign", - "fingerprint": "b0cf223ce05db6819480455fa2e6a0e0feb1a0fbf8e9abad0a7054492fb5304d", + "fingerprint": "4cedd525607544d02d4b31481358510b2ecb8b9c54cc6b0f749799e1011ff3f0", "typescript": { "status": "covered", "method": "MessagingClient.campaigns.create" @@ -1467,7 +1478,7 @@ "method": "POST", "path": "/messaging/projects/{projectSlug}/campaigns/{id}/launch", "operationId": "launchCampaign", - "fingerprint": "6e9f9bca026988228dc99cf7528085fe2358d5d4b6715d7fc5b86c4ce5738d82", + "fingerprint": "4af01830d796ffcfe37f6397cccc188c4546caf643b586a9925f137df9c7b4c2", "typescript": { "status": "covered", "method": "MessagingClient.campaigns.launch" @@ -1478,7 +1489,7 @@ "method": "POST", "path": "/messaging/projects/{projectSlug}/campaigns/{id}/pause", "operationId": "pauseCampaign", - "fingerprint": "e19f4b6e91f13d730302cdd33aa0e7362633ba9b490eeff2951de56cd74ba8b6", + "fingerprint": "cd056dc9925cf81e94b47a9a345e08287dd4d765ce9beb31ba13d78de5fd51ef", "typescript": { "status": "covered", "method": "MessagingClient.campaigns.pause" @@ -1500,18 +1511,29 @@ "method": "POST", "path": "/messaging/projects/{projectSlug}/campaigns/{id}/requeue", "operationId": "requeueCampaign", - "fingerprint": "e37311f5b865649731977696e24794d350b3fac493ae0be9cd47ce33f92782b2", + "fingerprint": "0ed764141ea7484211a62c62c4496c37572548109cc0a5355c94cb02661c4f7d", "typescript": { "status": "covered", "method": "MessagingClient.campaigns.requeue" } }, + { + "family": "messaging", + "method": "POST", + "path": "/messaging/projects/{projectSlug}/campaigns/{id}/reschedule", + "operationId": "rescheduleCampaign", + "fingerprint": "b6c239ea6b3e2dc5df21cccdde97afb239664089f6654170ae3db917d94e6676", + "typescript": { + "status": "covered", + "method": "MessagingClient.campaigns.reschedule" + } + }, { "family": "messaging", "method": "POST", "path": "/messaging/projects/{projectSlug}/campaigns/{id}/resume", "operationId": "resumeCampaign", - "fingerprint": "51232b6c94b80d3c20b4410a7eedf22e4de255414e26ed0c7b89efe96880c619", + "fingerprint": "9c0636666ce2e8678efc4a33a9b2fdac42435008f9a12486b57253c11df0764e", "typescript": { "status": "covered", "method": "MessagingClient.campaigns.resume" @@ -1522,7 +1544,7 @@ "method": "POST", "path": "/messaging/projects/{projectSlug}/campaigns/{id}/stop", "operationId": "stopCampaign", - "fingerprint": "b58b3b88eaddf3d7543fe136d5d667e9588e36d767830eb9cebcf53004d43b9c", + "fingerprint": "6a09b0d2a67631403abfcd8b4eac52c29b25cfeb873f91d3a7f82236e8ced7d1", "typescript": { "status": "covered", "method": "MessagingClient.campaigns.stop" @@ -1577,7 +1599,7 @@ "method": "POST", "path": "/messaging/testing/{projectId}/events", "operationId": "triggerTestEvent", - "fingerprint": "d0fa7c0f5b2dee4925898d46c1e4b9f26b75b6b8169616d862e87064d7e066b1", + "fingerprint": "b5257525dd5d250dd5df292894bf43b30c663c55a85c4c664033611e6f959118", "typescript": { "status": "covered", "method": "MessagingClient.testing.triggerEvent" @@ -4094,7 +4116,7 @@ "method": "PATCH", "path": "/platform/campaigns/{campaignId}", "operationId": "updateCampaign", - "fingerprint": "90baf7ea126eaea6a4bd8fddc729b24806db837492366c8d8798469545ef1214", + "fingerprint": "b7af610a57c1f24db76241bd0bf60dce7528c2b4f6e7b2c9a9da68f23ab0d380", "typescript": { "status": "covered", "method": "Client.campaigns.update" @@ -4217,7 +4239,7 @@ "method": "POST", "path": "/api/account/features/evaluate", "operationId": "evaluateAccountFeatures", - "fingerprint": "e17563fb6140a20ca812de3bdc508f74cf3ecc84f920ec36149fb85a35cf28f2", + "fingerprint": "65fea0db7dfde8d6ae7452fe139b7fc0ec89d46bfc2bf2cbd77cd7ad1a9a5b4e", "typescript": { "status": "excluded", "reason": "Dashboard identity and account controls require a live browser account session; not part of the machine SDK." @@ -4690,7 +4712,7 @@ "method": "POST", "path": "/platform/campaigns", "operationId": "createPlatformCampaign", - "fingerprint": "1bc165505e2850f060e7f191155ba64137b549161e713fce5f8ceed855e8e38c", + "fingerprint": "78e845758e756676217319c79cd02e6cdaf9ebbb5b034e5fe3f04b87a1398fc7", "typescript": { "status": "covered", "method": "Client.campaigns.create" @@ -4723,7 +4745,7 @@ "method": "POST", "path": "/platform/campaigns/{campaignId}/launch", "operationId": "launchPlatformCampaign", - "fingerprint": "044791fb098cb2e82eab04b812437ec23d5ea4158e77dc43e1003daab147b96c", + "fingerprint": "5769e0021324bd63c9d63a8a72c1094c228e5911c7ea1184e1d9bafc3928475a", "typescript": { "status": "covered", "method": "Client.campaigns.launch" @@ -4762,6 +4784,17 @@ "method": "Client.campaigns.requeue" } }, + { + "family": "platform", + "method": "POST", + "path": "/platform/campaigns/{campaignId}/reschedule", + "operationId": "reschedulePlatformCampaign", + "fingerprint": "21878cc0e7fed1e5f4c0f983a4899a70dd5fa4a410b1d293962a8ddccabe2f23", + "typescript": { + "status": "covered", + "method": "Client.campaigns.reschedule" + } + }, { "family": "platform", "method": "POST", @@ -5356,7 +5389,7 @@ "method": "POST", "path": "/platform/webhooks/{webhookId}/tests", "operationId": "testOrganizationWebhook", - "fingerprint": "ea8c68a8253924c3a1a025d1a04335c789e3791d2628bb37c399dd7070de39b1", + "fingerprint": "a35ce4d334055a22fc33b5fdd4dabc9d64cdbb512b536804a30e6c6b5687aa42", "typescript": { "status": "covered", "method": "Client.webhooks.test" diff --git a/contracts/openapi.messaging.json b/contracts/openapi.messaging.json index 95ab4ae7..f959f015 100644 --- a/contracts/openapi.messaging.json +++ b/contracts/openapi.messaging.json @@ -336,6 +336,16 @@ "interactive": { "type": "object", "additionalProperties": {} + }, + "referral": { + "type": "object", + "description": "Meta-supplied Click-to-WhatsApp entry point. Does not establish a conversion or payment.", + "properties": { + "source_type": { "type": "string" }, + "source_id": { "type": "string" }, + "source_url": { "type": "string" }, + "ctwa_clid": { "type": "string" } + } } }, "required": [ @@ -5366,905 +5376,315 @@ ], "additionalProperties": false }, - "BanSafeForecast": { + "BanSafeRequiredFinding": { "type": "object", "properties": { - "days7": { - "type": "number", - "minimum": 0, - "maximum": 1, - "description": "Probability (0-1) of a temporary or permanent ban within 7 days.", - "example": 0.02 + "findingKey": { + "type": "string", + "pattern": "^[a-z0-9_]{1,64}$", + "description": "Stable finding identifier.", + "example": "typing_absent" }, - "days14": { - "type": "number", - "minimum": 0, - "maximum": 1, - "description": "Probability (0-1) of a temporary or permanent ban within 14 days.", - "example": 0.06 + "title": { + "type": "string", + "minLength": 1, + "maxLength": 160, + "description": "Customer-facing finding title.", + "example": "Messages sent without showing that you are typing" }, - "days30": { - "type": "number", - "minimum": 0, - "maximum": 1, - "description": "Probability (0-1) of a temporary or permanent ban within 30 days.", - "example": 0.11 + "severity": { + "type": "string", + "enum": [ + "info", + "warning", + "critical" + ] } }, "required": [ - "days7", - "days14", - "days30" + "findingKey", + "title", + "severity" ], "additionalProperties": false }, - "BanSafeRiskFactor": { + "BanSafeActionPayload": { "type": "object", "properties": { - "key": { + "phoneNumber": { "type": "string", - "pattern": "^(?:group:)?[a-z0-9_]{1,64}$", - "description": "Stable factor identifier: a feature key, or \"group:\".", - "example": "cold_send_ratio" + "pattern": "^\\+[1-9]\\d{1,14}$", + "description": "The customer's own number in E.164 format.", + "example": "+15551234567" }, - "group": { + "action": { "type": "string", "enum": [ - "volume", - "cold_outreach", - "restrictions", - "engagement", - "send_errors", - "pattern", - "traffic_mix", - "number_age", - "connection", - "ban_history", - "account", - "workspace", - "climate", - "conversation", - "solicitation", - "reputation" + "applied", + "changed", + "lifted", + "daily_allowance_reached" ], - "description": "Factor group this factor belongs to.", - "example": "cold_outreach" + "description": "Whether the restriction was newly applied, moved, or removed. `daily_allowance_reached` reports that the number spent its warm-up allowance for the day; nothing on the ladder moved and sending resumes at the next reset." }, - "label": { + "scope": { "type": "string", - "minLength": 1, - "maxLength": 160, - "description": "Customer-facing factor label in WhatsApp vocabulary.", - "example": "Share of messages sent to people who never messaged you" + "enum": [ + "number", + "organization" + ], + "description": "Whether this covers one number or every number in the workspace." }, - "direction": { + "rung": { "type": "string", "enum": [ - "raises", - "lowers" + "none", + "notify", + "throttle", + "block_cold", + "suspend" ], - "description": "Whether the factor raises or lowers the risk." + "description": "Restriction now in force." }, - "strength": { + "previousRung": { "type": "string", + "nullable": true, "enum": [ - "strong", - "moderate", - "slight" + "none", + "notify", + "throttle", + "block_cold", + "suspend", + null + ], + "description": "Restriction previously in force, or null when none was." + }, + "reason": { + "type": "string", + "enum": [ + "health", + "finding", + "org_pattern", + "repeat", + "restriction", + "operator", + "health_model_cutover", + "warmup" ], - "description": "How strongly this factor moves the score." + "description": "Why the restriction changed." }, - "impact": { + "health": { "type": "integer", + "nullable": true, "minimum": 0, "maximum": 100, - "description": "This factor's share, as a whole percentage, of the raising (or lowering) total.", - "example": 42 + "description": "Measured health from 0 (worst) to 100 (best), or null when not measured.", + "example": 62 }, - "sentence": { + "healthBand": { "type": "string", - "minLength": 1, - "maxLength": 240, - "description": "Evidence sentence built from observed numbers.", - "example": "38 of the 51 people you messaged had never messaged this number" + "enum": [ + "good", + "fair", + "poor", + "failing", + "unknown" + ], + "description": "Health band matching health." + }, + "requires": { + "type": "array", + "items": { + "$ref": "#/components/schemas/BanSafeRequiredFinding" + }, + "maxItems": 25, + "description": "Findings that must resolve before this restriction lifts." + }, + "throughputPerMinute": { + "type": "integer", + "nullable": true, + "minimum": 0, + "maximum": 10000, + "description": "Messages per minute allowed while paced, or null when not paced.", + "example": 6 }, - "hint": { + "eligibleLiftAt": { "type": "string", "nullable": true, + "format": "date-time", + "description": "Earliest instant the restriction can step down, ISO 8601.", + "example": "2026-09-08T14:00:00.000Z" + }, + "liftRequires": { + "type": "string", + "minLength": 1, "maxLength": 240, - "description": "Action to take. Null for lowering factors and hint-less groups." + "description": "One sentence describing what lifts the restriction.", + "example": "Resolve the two open checks and keep them clean for six evaluations." + }, + "appealUrl": { + "type": "string", + "maxLength": 500, + "format": "uri", + "description": "Where to appeal this action.", + "example": "https://app.polymorfa.com/safety/appeal" + }, + "startedAt": { + "type": "string", + "format": "date-time", + "description": "ISO 8601 timestamp when this restriction took effect.", + "example": "2026-09-06T14:00:00.000Z" + }, + "docs": { + "type": "string", + "maxLength": 500, + "format": "uri", + "description": "Documentation for this restriction.", + "example": "https://docs.polymorfa.com/safety/restrictions" } }, "required": [ - "key", - "group", - "label", - "direction", - "strength", - "impact", - "sentence", - "hint" + "phoneNumber", + "action", + "scope", + "rung", + "previousRung", + "reason", + "health", + "healthBand", + "requires", + "throughputPerMinute", + "eligibleLiftAt", + "liftRequires", + "appealUrl", + "startedAt", + "docs" ], "additionalProperties": false }, - "BanSafeModelRef": { + "BanSafeActionEvent": { "type": "object", "properties": { - "version": { + "id": { "type": "string", - "minLength": 1, - "maxLength": 64, - "description": "Version of the forecast model that produced the evaluation.", - "example": "prior-v0" + "description": "Unique event identifier" }, - "reliability": { + "session": { + "type": "string", + "description": "Session that produced this event" + }, + "externalId": { + "type": "string", + "description": "Integrator reference from the QuickLink that created this session. Included on session lifecycle events when supplied." + }, + "timestamp": { + "type": "string", + "description": "ISO 8601 timestamp" + }, + "event": { "type": "string", "enum": [ - "prior", - "early", - "calibrated" + "bansafe.action" ], - "description": "Reliability statement: prior (expert prior, no training data), early (trained on a small corpus), calibrated (trained and calibrated on the ledger)." + "description": "Event type" + }, + "payload": { + "$ref": "#/components/schemas/BanSafeActionPayload" } }, "required": [ - "version", - "reliability" + "id", + "session", + "timestamp", + "event", + "payload" ], "additionalProperties": false }, - "BanSafeRiskChangedPayload": { + "BanSafeHealthThresholdPayload": { "type": "object", "properties": { - "phoneNumber": { + "sessionId": { "type": "string", - "pattern": "^\\+[1-9]\\d{1,14}$", - "description": "The customer's own number in E.164 format.", - "example": "+15551234567" + "format": "uuid", + "description": "Session whose present Health crossed the project threshold." }, - "level": { + "projectId": { "type": "string", - "enum": [ - "low", - "elevated", - "high", - "critical" - ], - "description": "Current risk level." + "format": "uuid", + "description": "Project whose Health rule produced this action." }, - "previousLevel": { - "type": "string", - "nullable": true, - "enum": [ - "low", - "elevated", - "high", - "critical", - null - ], - "description": "Previous risk level, or null for the first evaluation of the number." + "health": { + "type": "number", + "minimum": 0, + "maximum": 100, + "description": "Present Health; higher is healthier." }, - "score": { + "threshold": { "type": "integer", "minimum": 0, "maximum": 100, - "description": "Risk score from 0 (lowest) to 100 (highest).", - "example": 37 + "description": "Configured project threshold crossed by this evaluation." }, - "forecast": { - "$ref": "#/components/schemas/BanSafeForecast" + "healthSource": { + "type": "string", + "enum": [ + "rules_v1", + "ml_model" + ], + "description": "Estimator family used by the evaluation." }, - "factors": { - "type": "array", - "items": { - "$ref": "#/components/schemas/BanSafeRiskFactor" - }, - "maxItems": 5, - "description": "Up to five contributing factors, ordered by impact." + "estimatorVersion": { + "type": "string", + "minLength": 1, + "maxLength": 128, + "description": "Estimator version used by the evaluation." }, - "model": { - "$ref": "#/components/schemas/BanSafeModelRef" + "modelVersion": { + "type": "string", + "nullable": true, + "minLength": 1, + "maxLength": 128, + "description": "Learned-model version, or null for a rules-based estimate." }, "evaluatedAt": { "type": "string", "format": "date-time", - "description": "ISO 8601 timestamp of the evaluation that produced this event.", - "example": "2026-09-06T14:00:00.000Z" + "description": "Evaluation time in ISO 8601 format." + }, + "policyVersion": { + "type": "integer", + "minimum": 1, + "maximum": 9007199254740991, + "description": "Project Health rule version evaluated." + }, + "episodeId": { + "type": "string", + "format": "uuid", + "description": "Stable below-threshold episode identifier." + }, + "actionId": { + "type": "string", + "format": "uuid", + "description": "Stable event identity used for retries." } }, "required": [ - "phoneNumber", - "level", - "previousLevel", - "score", - "forecast", - "factors", - "model", - "evaluatedAt" + "sessionId", + "projectId", + "health", + "threshold", + "healthSource", + "estimatorVersion", + "modelVersion", + "evaluatedAt", + "policyVersion", + "episodeId", + "actionId" ], "additionalProperties": false }, - "BanSafeRiskChangedEvent": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Unique event identifier" - }, - "session": { - "type": "string", - "description": "Session that produced this event" - }, - "externalId": { - "type": "string", - "description": "Integrator reference from the QuickLink that created this session. Included on session lifecycle events when supplied." - }, - "timestamp": { - "type": "string", - "description": "ISO 8601 timestamp" - }, - "event": { - "type": "string", - "enum": [ - "bansafe.risk_changed" - ], - "description": "Event type" - }, - "payload": { - "$ref": "#/components/schemas/BanSafeRiskChangedPayload" - } - }, - "required": [ - "id", - "session", - "timestamp", - "event", - "payload" - ], - "additionalProperties": false - }, - "BanSafeHealthPenalties": { - "type": "object", - "properties": { - "conduct": { - "type": "integer", - "minimum": 0, - "maximum": 100 - }, - "restriction": { - "type": "integer", - "minimum": 0, - "maximum": 100 - }, - "connection": { - "type": "integer", - "minimum": 0, - "maximum": 100 - } - }, - "required": [ - "conduct", - "restriction", - "connection" - ], - "additionalProperties": false - }, - "BanSafeHealthFinding": { - "type": "object", - "properties": { - "key": { - "type": "string", - "pattern": "^[a-z0-9_]{1,64}$", - "description": "Stable finding identifier.", - "example": "unsolicited_outreach" - }, - "title": { - "type": "string", - "minLength": 1, - "maxLength": 160, - "description": "Customer-facing finding title.", - "example": "Messaging people who never wrote to you" - }, - "severity": { - "type": "string", - "enum": [ - "info", - "warning", - "critical" - ] - }, - "status": { - "type": "string", - "enum": [ - "open", - "acknowledged", - "not_measured" - ], - "description": "not_measured means the signal could not be measured for this number; it is never reported as clean." - }, - "points": { - "type": "integer", - "minimum": 0, - "maximum": 25, - "description": "Health points this finding costs.", - "example": 8 - } - }, - "required": [ - "key", - "title", - "severity", - "status", - "points" - ], - "additionalProperties": false - }, - "BanSafeHealthChangedPayload": { - "type": "object", - "properties": { - "phoneNumber": { - "type": "string", - "pattern": "^\\+[1-9]\\d{1,14}$", - "description": "The customer's own number in E.164 format.", - "example": "+15551234567" - }, - "health": { - "type": "integer", - "nullable": true, - "minimum": 0, - "maximum": 100, - "description": "Measured health from 0 (worst) to 100 (best), or null when not measured.", - "example": 62 - }, - "band": { - "type": "string", - "enum": [ - "good", - "fair", - "poor", - "failing", - "unknown" - ], - "description": "Health band matching health." - }, - "previousBand": { - "type": "string", - "nullable": true, - "enum": [ - "good", - "fair", - "poor", - "failing", - "unknown", - null - ], - "description": "Previous band, or null for the first evaluation of the number." - }, - "state": { - "type": "string", - "enum": [ - "measured", - "partial", - "measuring", - "restricted", - "banned" - ], - "description": "Data sufficiency of this evaluation." - }, - "penalties": { - "$ref": "#/components/schemas/BanSafeHealthPenalties" - }, - "findings": { - "type": "array", - "items": { - "$ref": "#/components/schemas/BanSafeHealthFinding" - }, - "maxItems": 50, - "description": "Findings behind the score, with the points each one costs." - }, - "measuredChecks": { - "type": "integer", - "minimum": 0, - "maximum": 200, - "description": "Catalogue checks that could be measured for this number.", - "example": 14 - }, - "totalChecks": { - "type": "integer", - "minimum": 0, - "maximum": 200, - "description": "Catalogue checks in total.", - "example": 18 - }, - "allowance": { - "type": "integer", - "nullable": true, - "minimum": 0, - "description": "Messages this number may send today under the project's warm-up plan, recomputed for the new health band. Null or absent when the project has no warm-up plan.", - "example": 240 - }, - "evaluatedAt": { - "type": "string", - "format": "date-time", - "description": "ISO 8601 timestamp of the evaluation that produced this event.", - "example": "2026-09-06T14:00:00.000Z" - } - }, - "required": [ - "phoneNumber", - "health", - "band", - "previousBand", - "state", - "penalties", - "findings", - "measuredChecks", - "totalChecks", - "evaluatedAt" - ], - "additionalProperties": false - }, - "BanSafeHealthChangedEvent": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Unique event identifier" - }, - "session": { - "type": "string", - "description": "Session that produced this event" - }, - "externalId": { - "type": "string", - "description": "Integrator reference from the QuickLink that created this session. Included on session lifecycle events when supplied." - }, - "timestamp": { - "type": "string", - "description": "ISO 8601 timestamp" - }, - "event": { - "type": "string", - "enum": [ - "bansafe.health_changed" - ], - "description": "Event type" - }, - "payload": { - "$ref": "#/components/schemas/BanSafeHealthChangedPayload" - } - }, - "required": [ - "id", - "session", - "timestamp", - "event", - "payload" - ], - "additionalProperties": false - }, - "BanSafeEnforcementPayload": { - "type": "object", - "properties": { - "phoneNumber": { - "type": "string", - "pattern": "^\\+[1-9]\\d{1,14}$", - "description": "The customer's own number in E.164 format.", - "example": "+15551234567" - }, - "kind": { - "type": "string", - "enum": [ - "cap_warning", - "cap_reached", - "timelock", - "temporary_ban", - "permanent_ban", - "connect_blocked", - "customer_report" - ], - "description": "Enforcement outcome, or customer_report for a customer-reported ban." - }, - "source": { - "type": "string", - "enum": [ - "runtime", - "customer" - ], - "description": "Whether Polymorfa observed the enforcement or the customer reported it." - }, - "code": { - "type": "integer", - "minimum": 0, - "description": "WhatsApp status code (for example 402, 403, 406, 475) when observed.", - "example": 402 - }, - "subCode": { - "type": "integer", - "minimum": 0, - "description": "WhatsApp sub-code (101-106 for temporary bans) when provided.", - "example": 101 - }, - "reason": { - "type": "string", - "maxLength": 200, - "description": "Sanitised reason from the WhatsApp code table.", - "example": "Suspected spam activity" - }, - "enforcementType": { - "type": "string", - "maxLength": 64, - "description": "WhatsApp enforcement type (for example BIZ_QUALITY) when provided." - }, - "startedAt": { - "type": "string", - "format": "date-time", - "description": "ISO 8601 timestamp when the enforcement started.", - "example": "2026-09-06T14:03:12.000Z" - }, - "endsAt": { - "type": "string", - "format": "date-time", - "description": "ISO 8601 timestamp when the enforcement lifts, when WhatsApp provides a duration.", - "example": "2026-09-07T14:03:12.000Z" - } - }, - "required": [ - "phoneNumber", - "kind", - "source", - "startedAt" - ], - "additionalProperties": false - }, - "BanSafeEnforcementEvent": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Unique event identifier" - }, - "session": { - "type": "string", - "description": "Session that produced this event" - }, - "externalId": { - "type": "string", - "description": "Integrator reference from the QuickLink that created this session. Included on session lifecycle events when supplied." - }, - "timestamp": { - "type": "string", - "description": "ISO 8601 timestamp" - }, - "event": { - "type": "string", - "enum": [ - "bansafe.enforcement" - ], - "description": "Event type" - }, - "payload": { - "$ref": "#/components/schemas/BanSafeEnforcementPayload" - } - }, - "required": [ - "id", - "session", - "timestamp", - "event", - "payload" - ], - "additionalProperties": false - }, - "BanSafeRequiredFinding": { - "type": "object", - "properties": { - "findingKey": { - "type": "string", - "pattern": "^[a-z0-9_]{1,64}$", - "description": "Stable finding identifier.", - "example": "typing_absent" - }, - "title": { - "type": "string", - "minLength": 1, - "maxLength": 160, - "description": "Customer-facing finding title.", - "example": "Messages sent without showing that you are typing" - }, - "severity": { - "type": "string", - "enum": [ - "info", - "warning", - "critical" - ] - } - }, - "required": [ - "findingKey", - "title", - "severity" - ], - "additionalProperties": false - }, - "BanSafeActionPayload": { - "type": "object", - "properties": { - "phoneNumber": { - "type": "string", - "pattern": "^\\+[1-9]\\d{1,14}$", - "description": "The customer's own number in E.164 format.", - "example": "+15551234567" - }, - "action": { - "type": "string", - "enum": [ - "applied", - "changed", - "lifted", - "daily_allowance_reached" - ], - "description": "Whether the restriction was newly applied, moved, or removed. `daily_allowance_reached` reports that the number spent its warm-up allowance for the day; nothing on the ladder moved and sending resumes at the next reset." - }, - "scope": { - "type": "string", - "enum": [ - "number", - "organization" - ], - "description": "Whether this covers one number or every number in the workspace." - }, - "rung": { - "type": "string", - "enum": [ - "none", - "notify", - "throttle", - "block_cold", - "suspend" - ], - "description": "Restriction now in force." - }, - "previousRung": { - "type": "string", - "nullable": true, - "enum": [ - "none", - "notify", - "throttle", - "block_cold", - "suspend", - null - ], - "description": "Restriction previously in force, or null when none was." - }, - "reason": { - "type": "string", - "enum": [ - "health", - "finding", - "org_pattern", - "repeat", - "restriction", - "operator", - "health_model_cutover", - "warmup" - ], - "description": "Why the restriction changed." - }, - "health": { - "type": "integer", - "nullable": true, - "minimum": 0, - "maximum": 100, - "description": "Measured health from 0 (worst) to 100 (best), or null when not measured.", - "example": 62 - }, - "healthBand": { - "type": "string", - "enum": [ - "good", - "fair", - "poor", - "failing", - "unknown" - ], - "description": "Health band matching health." - }, - "requires": { - "type": "array", - "items": { - "$ref": "#/components/schemas/BanSafeRequiredFinding" - }, - "maxItems": 25, - "description": "Findings that must resolve before this restriction lifts." - }, - "throughputPerMinute": { - "type": "integer", - "nullable": true, - "minimum": 0, - "maximum": 10000, - "description": "Messages per minute allowed while paced, or null when not paced.", - "example": 6 - }, - "eligibleLiftAt": { - "type": "string", - "nullable": true, - "format": "date-time", - "description": "Earliest instant the restriction can step down, ISO 8601.", - "example": "2026-09-08T14:00:00.000Z" - }, - "liftRequires": { - "type": "string", - "minLength": 1, - "maxLength": 240, - "description": "One sentence describing what lifts the restriction.", - "example": "Resolve the two open checks and keep them clean for six evaluations." - }, - "appealUrl": { - "type": "string", - "maxLength": 500, - "format": "uri", - "description": "Where to appeal this action.", - "example": "https://app.polymorfa.com/safety/appeal" - }, - "startedAt": { - "type": "string", - "format": "date-time", - "description": "ISO 8601 timestamp when this restriction took effect.", - "example": "2026-09-06T14:00:00.000Z" - }, - "docs": { - "type": "string", - "maxLength": 500, - "format": "uri", - "description": "Documentation for this restriction.", - "example": "https://docs.polymorfa.com/safety/restrictions" - } - }, - "required": [ - "phoneNumber", - "action", - "scope", - "rung", - "previousRung", - "reason", - "health", - "healthBand", - "requires", - "throughputPerMinute", - "eligibleLiftAt", - "liftRequires", - "appealUrl", - "startedAt", - "docs" - ], - "additionalProperties": false - }, - "BanSafeActionEvent": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Unique event identifier" - }, - "session": { - "type": "string", - "description": "Session that produced this event" - }, - "externalId": { - "type": "string", - "description": "Integrator reference from the QuickLink that created this session. Included on session lifecycle events when supplied." - }, - "timestamp": { - "type": "string", - "description": "ISO 8601 timestamp" - }, - "event": { - "type": "string", - "enum": [ - "bansafe.action" - ], - "description": "Event type" - }, - "payload": { - "$ref": "#/components/schemas/BanSafeActionPayload" - } - }, - "required": [ - "id", - "session", - "timestamp", - "event", - "payload" - ], - "additionalProperties": false - }, - "BanSafeHealthThresholdPayload": { - "type": "object", - "properties": { - "sessionId": { - "type": "string", - "format": "uuid", - "description": "Session whose present Health crossed the project threshold." - }, - "projectId": { - "type": "string", - "format": "uuid", - "description": "Project whose Health rule produced this action." - }, - "health": { - "type": "number", - "minimum": 0, - "maximum": 100, - "description": "Present Health; higher is healthier." - }, - "threshold": { - "type": "integer", - "minimum": 0, - "maximum": 100, - "description": "Configured project threshold crossed by this evaluation." - }, - "healthSource": { - "type": "string", - "enum": [ - "rules_v1", - "ml_model" - ], - "description": "Estimator family used by the evaluation." - }, - "estimatorVersion": { - "type": "string", - "minLength": 1, - "maxLength": 128, - "description": "Estimator version used by the evaluation." - }, - "modelVersion": { - "type": "string", - "nullable": true, - "minLength": 1, - "maxLength": 128, - "description": "Learned-model version, or null for a rules-based estimate." - }, - "evaluatedAt": { - "type": "string", - "format": "date-time", - "description": "Evaluation time in ISO 8601 format." - }, - "policyVersion": { - "type": "integer", - "minimum": 1, - "maximum": 9007199254740991, - "description": "Project Health rule version evaluated." - }, - "episodeId": { - "type": "string", - "format": "uuid", - "description": "Stable below-threshold episode identifier." - }, - "actionId": { - "type": "string", - "format": "uuid", - "description": "Stable event identity used for retries." - } - }, - "required": [ - "sessionId", - "projectId", - "health", - "threshold", - "healthSource", - "estimatorVersion", - "modelVersion", - "evaluatedAt", - "policyVersion", - "episodeId", - "actionId" - ], - "additionalProperties": false - }, - "BanSafeHealthThresholdEvent": { + "BanSafeHealthThresholdEvent": { "type": "object", "properties": { "id": { @@ -6957,12 +6377,79 @@ "event": { "type": "string", "enum": [ - "campaign.resumed" + "campaign.resumed" + ], + "description": "Event type" + }, + "payload": { + "$ref": "#/components/schemas/CampaignResumedPayload" + } + }, + "required": [ + "id", + "session", + "timestamp", + "event", + "payload" + ] + }, + "CampaignStoppedPayload": { + "type": "object", + "properties": { + "campaignId": { + "type": "string", + "description": "Campaign id", + "example": "01936b2e-5f2a-7c1d-9a3e-8b4c2d1e0f11" + }, + "sentCount": { + "type": "integer", + "minimum": 0 + }, + "abandonedCount": { + "type": "integer", + "minimum": 0 + }, + "stoppedAt": { + "type": "integer", + "minimum": 0, + "description": "Unix timestamp in milliseconds when the stop settled" + } + }, + "required": [ + "campaignId", + "sentCount", + "abandonedCount", + "stoppedAt" + ] + }, + "CampaignStoppedEvent": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique event identifier" + }, + "session": { + "type": "string", + "description": "Session that produced this event" + }, + "externalId": { + "type": "string", + "description": "Integrator reference from the QuickLink that created this session. Included on session lifecycle events when supplied." + }, + "timestamp": { + "type": "string", + "description": "ISO 8601 timestamp" + }, + "event": { + "type": "string", + "enum": [ + "campaign.stopped" ], "description": "Event type" }, "payload": { - "$ref": "#/components/schemas/CampaignResumedPayload" + "$ref": "#/components/schemas/CampaignStoppedPayload" } }, "required": [ @@ -6973,7 +6460,7 @@ "payload" ] }, - "CampaignStoppedPayload": { + "CampaignRescheduledPayload": { "type": "object", "properties": { "campaignId": { @@ -6981,28 +6468,31 @@ "description": "Campaign id", "example": "01936b2e-5f2a-7c1d-9a3e-8b4c2d1e0f11" }, - "sentCount": { + "previousScheduledAt": { "type": "integer", - "minimum": 0 + "nullable": true, + "minimum": 0, + "description": "The start time that was replaced, in Unix milliseconds" }, - "abandonedCount": { + "scheduledAt": { "type": "integer", - "minimum": 0 + "minimum": 0, + "description": "The new start time in Unix milliseconds. Equal to `rescheduledAt` when the campaign was asked to start now." }, - "stoppedAt": { + "rescheduledAt": { "type": "integer", "minimum": 0, - "description": "Unix timestamp in milliseconds when the stop settled" + "description": "Unix timestamp in milliseconds when the new start time was accepted" } }, "required": [ "campaignId", - "sentCount", - "abandonedCount", - "stoppedAt" + "previousScheduledAt", + "scheduledAt", + "rescheduledAt" ] }, - "CampaignStoppedEvent": { + "CampaignRescheduledEvent": { "type": "object", "properties": { "id": { @@ -7024,12 +6514,12 @@ "event": { "type": "string", "enum": [ - "campaign.stopped" + "campaign.rescheduled" ], "description": "Event type" }, "payload": { - "$ref": "#/components/schemas/CampaignStoppedPayload" + "$ref": "#/components/schemas/CampaignRescheduledPayload" } }, "required": [ @@ -16484,6 +15974,55 @@ "name" ] }, + "UpdateCampaignResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "data": { + "$ref": "#/components/schemas/Campaign" + } + }, + "required": [ + "success", + "data" + ] + }, + "UpdateCampaignRequest": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 200, + "description": "New name, 1 to 200 characters after trimming." + }, + "recipientListId": { + "type": "string", + "nullable": true, + "format": "uuid", + "description": "Audience to send to, or null for none. Only an unlaunched draft can change it; the campaign's current recipients are replaced by the audience's members at launch." + }, + "senderConfig": { + "type": "object", + "additionalProperties": {}, + "description": "Replaces the sender selection and throughput. On an unlaunched draft it passes the same checks as create. Once the campaign has launched it must pass the launch checks." + }, + "scheduledAt": { + "type": "integer", + "nullable": true, + "minimum": 0, + "maximum": 8640000000000000, + "description": "Start time in milliseconds since the Unix epoch, or null to start at launch. Only an unlaunched draft can change it." + } + }, + "additionalProperties": false, + "minProperties": 1 + }, "CampaignOperation": { "allOf": [ { @@ -16604,6 +16143,41 @@ "data" ] }, + "RescheduleCampaignResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "data": { + "$ref": "#/components/schemas/CampaignOperation" + } + }, + "required": [ + "success", + "data" + ] + }, + "RescheduleCampaignRequest": { + "type": "object", + "properties": { + "scheduledAt": { + "type": "integer", + "nullable": true, + "minimum": 0, + "exclusiveMinimum": true, + "maximum": 8640000000000000, + "description": "The new start time as a Unix timestamp in milliseconds. `null`, or a time that is not in the future, starts the campaign now." + } + }, + "required": [ + "scheduledAt" + ], + "additionalProperties": false + }, "RequeueCampaignResponse": { "type": "object", "properties": { @@ -18105,9 +17679,7 @@ "call.ended", "session.status", "session.restriction_updated", - "template.status", - "bansafe.enforcement", - "bansafe.risk_changed" + "template.status" ] }, "description": { @@ -18146,9 +17718,7 @@ "call.ended", "session.status", "session.restriction_updated", - "template.status", - "bansafe.enforcement", - "bansafe.risk_changed" + "template.status" ] }, "session": { @@ -18294,24 +17864,6 @@ "type": "string", "minLength": 1, "maxLength": 256 - }, - "enforcementKind": { - "type": "string", - "enum": [ - "cap_warning", - "cap_reached", - "timelock", - "temporary_ban" - ] - }, - "riskLevel": { - "type": "string", - "enum": [ - "low", - "elevated", - "high", - "critical" - ] } }, "additionalProperties": false @@ -18336,9 +17888,7 @@ "call.ended", "session.status", "session.restriction_updated", - "template.status", - "bansafe.enforcement", - "bansafe.risk_changed" + "template.status" ], "description": "Fixture name. Each name is the event type it produces." }, @@ -18669,6 +18219,12 @@ "type": "string", "nullable": true }, + "experimentRevision": { + "type": "integer", + "minimum": 1, + "nullable": true, + "description": "Published experiment policy revision for an assigned native experiment; null otherwise." + }, "experimentAssigned": { "type": "boolean" } @@ -18679,6 +18235,7 @@ "available", "enrolled", "variant", + "experimentRevision", "experimentAssigned" ] }, @@ -18811,7 +18368,7 @@ "Pairing" ], "summary": "Get QR code for device pairing", - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `sessions:manage`\n\nEnterprise only. Your team must have direct-pairing access enabled. Use [QuickLink](/integrations/quicklink) for standard connections. Requests without direct-pairing access return 403.", + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `sessions:manage`\n\nDirect-pairing access must cover this Number. It may be included in an Enterprise or custom agreement. Use [QuickLink](/integrations/quicklink) when direct pairing is unavailable. Requests without access return 403.", "security": [ { "BearerAuth": [] @@ -18901,7 +18458,7 @@ } }, "403": { - "description": "Enterprise direct-pairing access is required. Use QuickLink for standard connections.", + "description": "Direct-pairing access is required for this Number. Use QuickLink when it is unavailable.", "content": { "application/json": { "schema": { @@ -18979,7 +18536,7 @@ "Pairing" ], "summary": "Request a pairing code for a phone number", - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `sessions:manage`\n\nEnterprise only. Your team must have direct-pairing access enabled. Use [QuickLink](/integrations/quicklink) for standard connections. Requests without direct-pairing access return 403.", + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `sessions:manage`\n\nDirect-pairing access must cover this Number. It may be included in an Enterprise or custom agreement. Use [QuickLink](/integrations/quicklink) when direct pairing is unavailable. Requests without access return 403.", "security": [ { "BearerAuth": [] @@ -19059,7 +18616,7 @@ } }, "403": { - "description": "Enterprise direct-pairing access is required. Use QuickLink for standard connections.", + "description": "Direct-pairing access is required for this Number. Use QuickLink when it is unavailable.", "content": { "application/json": { "schema": { @@ -37332,27 +36889,185 @@ } }, "x-mint": { - "content": "Linked Device API channels:manage" + "content": "Linked Device API channels:manage" + }, + "x-protocols": [ + "Linked Device API" + ], + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `channels:manage`" + } + }, + "/messaging/{session}/channels/{id}/live-updates": { + "post": { + "operationId": "subscribeChannelLiveUpdates", + "tags": [ + "Channels" + ], + "summary": "Subscribe temporarily to channel live updates", + "security": [ + { + "BearerAuth": [] + } + ], + "x-required-scope": "channels:read", + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "session", + "in": "path" + }, + { + "schema": { + "type": "string", + "pattern": "^[1-9][0-9]{0,18}$" + }, + "required": true, + "name": "id", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Live-update subscription duration", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChannelLiveUpdatesResponse" + } + } + } + }, + "400": { + "description": "Bad request — request body or params failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "invalid_parameter", + "message": "Correct the invalid request field.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" + } + } + } + }, + "401": { + "description": "Missing or invalid authentication token", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "authentication_error", + "code": "invalid_credential", + "message": "Supply a valid credential.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" + } + } + } + }, + "403": { + "description": "Authenticated but not permitted (missing scope or rule violation)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "permission_error", + "code": "permission_denied", + "message": "The credential does not have access to this resource.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#permission-denied" + } + } + } + }, + "404": { + "description": "Resource not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "resource_not_found", + "message": "The resource is unavailable to this caller.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "internal_error", + "message": "An unexpected service failure occurred. Keep the request ID for support.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#internal-error" + } + } + } + } + }, + "x-mint": { + "content": "Linked Device API channels:read" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `channels:manage`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `channels:read`" } }, - "/messaging/{session}/channels/{id}/live-updates": { + "/messaging/{session}/channels/{id}/follow": { "post": { - "operationId": "subscribeChannelLiveUpdates", + "operationId": "followChannel", "tags": [ "Channels" ], - "summary": "Subscribe temporarily to channel live updates", + "summary": "Follow a channel", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "channels:read", + "x-required-scope": "channels:manage", "parameters": [ { "schema": { @@ -37374,11 +37089,11 @@ ], "responses": { "200": { - "description": "Live-update subscription duration", + "description": "Channel followed", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ChannelLiveUpdatesResponse" + "$ref": "#/components/schemas/SuccessResponse" } } } @@ -37490,21 +37205,21 @@ } }, "x-mint": { - "content": "Linked Device API channels:read" + "content": "Linked Device API channels:manage" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `channels:read`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `channels:manage`" } }, - "/messaging/{session}/channels/{id}/follow": { + "/messaging/{session}/channels/{id}/unfollow": { "post": { - "operationId": "followChannel", + "operationId": "unfollowChannel", "tags": [ "Channels" ], - "summary": "Follow a channel", + "summary": "Unfollow a channel", "security": [ { "BearerAuth": [] @@ -37532,7 +37247,7 @@ ], "responses": { "200": { - "description": "Channel followed", + "description": "Channel unfollowed", "content": { "application/json": { "schema": { @@ -37656,13 +37371,13 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `channels:manage`" } }, - "/messaging/{session}/channels/{id}/unfollow": { + "/messaging/{session}/channels/{id}/mute": { "post": { - "operationId": "unfollowChannel", + "operationId": "muteChannel", "tags": [ "Channels" ], - "summary": "Unfollow a channel", + "summary": "Mute a channel", "security": [ { "BearerAuth": [] @@ -37690,7 +37405,7 @@ ], "responses": { "200": { - "description": "Channel unfollowed", + "description": "Channel muted", "content": { "application/json": { "schema": { @@ -37814,13 +37529,13 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `channels:manage`" } }, - "/messaging/{session}/channels/{id}/mute": { + "/messaging/{session}/channels/{id}/unmute": { "post": { - "operationId": "muteChannel", + "operationId": "unmuteChannel", "tags": [ "Channels" ], - "summary": "Mute a channel", + "summary": "Unmute a channel", "security": [ { "BearerAuth": [] @@ -37848,7 +37563,7 @@ ], "responses": { "200": { - "description": "Channel muted", + "description": "Channel unmuted", "content": { "application/json": { "schema": { @@ -37972,19 +37687,19 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `channels:manage`" } }, - "/messaging/{session}/channels/{id}/unmute": { + "/messaging/{session}/presence": { "post": { - "operationId": "unmuteChannel", + "operationId": "setPresence", "tags": [ - "Channels" + "Presence" ], - "summary": "Unmute a channel", + "summary": "Set the session's own presence (available / unavailable)", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "channels:manage", + "x-required-scope": "presence:write", "parameters": [ { "schema": { @@ -37993,20 +37708,20 @@ "required": true, "name": "session", "in": "path" - }, - { - "schema": { - "type": "string", - "pattern": "^[1-9][0-9]{0,18}$" - }, - "required": true, - "name": "id", - "in": "path" } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SetPresenceRequest" + } + } + } + }, "responses": { "200": { - "description": "Channel unmuted", + "description": "Presence updated", "content": { "application/json": { "schema": { @@ -38015,6 +37730,16 @@ } } }, + "202": { + "description": "Accepted for asynchronous processing in response to `Prefer: respond-async`; the result is not included", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AsyncAcceptedResponse" + } + } + } + }, "400": { "description": "Bad request — request body or params failed validation", "content": { @@ -38122,27 +37847,25 @@ } }, "x-mint": { - "content": "Linked Device API channels:manage" + "content": "Linked Device API presence:write" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `channels:manage`" - } - }, - "/messaging/{session}/presence": { - "post": { - "operationId": "setPresence", + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `presence:write`" + }, + "get": { + "operationId": "getPresence", "tags": [ "Presence" ], - "summary": "Set the session's own presence (available / unavailable)", + "summary": "Get the session's own presence", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "presence:write", + "x-required-scope": "presence:read", "parameters": [ { "schema": { @@ -38153,32 +37876,170 @@ "in": "path" } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SetPresenceRequest" - } - } - } - }, "responses": { "200": { - "description": "Presence updated", + "description": "Presence info", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/GetPresenceResponse" } } } }, - "202": { - "description": "Accepted for asynchronous processing in response to `Prefer: respond-async`; the result is not included", + "400": { + "description": "Bad request — request body or params failed validation", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AsyncAcceptedResponse" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "invalid_parameter", + "message": "Correct the invalid request field.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" + } + } + } + }, + "401": { + "description": "Missing or invalid authentication token", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "authentication_error", + "code": "invalid_credential", + "message": "Supply a valid credential.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" + } + } + } + }, + "403": { + "description": "Authenticated but not permitted (missing scope or rule violation)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "permission_error", + "code": "permission_denied", + "message": "The credential does not have access to this resource.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#permission-denied" + } + } + } + }, + "404": { + "description": "Resource not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "resource_not_found", + "message": "The resource is unavailable to this caller.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "internal_error", + "message": "An unexpected service failure occurred. Keep the request ID for support.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#internal-error" + } + } + } + } + }, + "x-mint": { + "content": "Linked Device API presence:read" + }, + "x-protocols": [ + "Linked Device API" + ], + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `presence:read`" + } + }, + "/messaging/{session}/presence/{conversation}": { + "get": { + "operationId": "getChatPresence", + "tags": [ + "Presence" + ], + "summary": "Get presence for a specific chat", + "security": [ + { + "BearerAuth": [] + } + ], + "x-required-scope": "presence:read", + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "session", + "in": "path" + }, + { + "schema": { + "type": "string" + }, + "required": true, + "name": "conversation", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Chat presence", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetChatPresenceResponse" } } } @@ -38290,25 +38151,27 @@ } }, "x-mint": { - "content": "Linked Device API presence:write" + "content": "Linked Device API presence:read" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `presence:write`" - }, - "get": { - "operationId": "getPresence", + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `presence:read`" + } + }, + "/messaging/{session}/presence/{conversation}/subscribe": { + "post": { + "operationId": "subscribePresence", "tags": [ "Presence" ], - "summary": "Get the session's own presence", + "summary": "Subscribe to presence updates for a chat", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "presence:read", + "x-required-scope": "presence:observe", "parameters": [ { "schema": { @@ -38317,15 +38180,33 @@ "required": true, "name": "session", "in": "path" + }, + { + "schema": { + "type": "string" + }, + "required": true, + "name": "conversation", + "in": "path" } ], "responses": { "200": { - "description": "Presence info", + "description": "Subscribed", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetPresenceResponse" + "$ref": "#/components/schemas/SubscribePresenceResponse" + } + } + } + }, + "202": { + "description": "Accepted for asynchronous processing in response to `Prefer: respond-async`; the result is not included", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AsyncAcceptedResponse" } } } @@ -38414,6 +38295,27 @@ } } }, + "429": { + "description": "Observation limit exceeded, or the session is inside the suspension window that follows", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "rate_limit_error", + "code": "rate_limit_exceeded", + "message": "The request limit was reached. Follow Retry-After when supplied.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#rate-limit-exceeded" + } + } + } + }, "500": { "description": "Internal server error", "content": { @@ -38437,52 +38339,45 @@ } }, "x-mint": { - "content": "Linked Device API presence:read" + "content": "Linked Device API presence:observe" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `presence:read`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `presence:observe`" } }, - "/messaging/{session}/presence/{conversation}": { + "/messaging/projects/{projectId}/safe-mode": { "get": { - "operationId": "getChatPresence", + "operationId": "getProjectSafeMode", "tags": [ - "Presence" + "Safe Mode" ], - "summary": "Get presence for a specific chat", + "summary": "Get a project's Safe Mode ceiling", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "presence:read", + "x-required-scope": "sessions:read", "parameters": [ { "schema": { - "type": "string" - }, - "required": true, - "name": "session", - "in": "path" - }, - { - "schema": { - "type": "string" + "type": "string", + "format": "uuid" }, "required": true, - "name": "conversation", + "name": "projectId", "in": "path" } ], "responses": { "200": { - "description": "Chat presence", + "description": "Project Safe Mode ceiling, and whether the plan includes BanSafe Lite", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetChatPresenceResponse" + "$ref": "#/components/schemas/GetProjectSafeModeResponse" } } } @@ -38594,62 +38489,95 @@ } }, "x-mint": { - "content": "Linked Device API presence:read" + "content": "sessions:read" }, - "x-protocols": [ - "Linked Device API" - ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `presence:read`" - } - }, - "/messaging/{session}/presence/{conversation}/subscribe": { - "post": { - "operationId": "subscribePresence", + "description": "**Required scope:** `sessions:read`" + }, + "put": { + "operationId": "updateProjectSafeMode", "tags": [ - "Presence" + "Safe Mode" ], - "summary": "Subscribe to presence updates for a chat", + "summary": "Set a project's Safe Mode ceiling", + "description": "**Required scope:** `sessions:manage`\n\nSets the most visible behaviour any number in this project may use. Presence and read receipts are visible to the people this number messages: turning them on makes the number appear online, exposes last-seen subject to the account's privacy settings, and puts blue ticks on incoming messages. Every axis is off by default and only this request changes it.", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "presence:observe", + "x-required-scope": "sessions:manage", "parameters": [ { "schema": { - "type": "string" - }, - "required": true, - "name": "session", - "in": "path" - }, - { - "schema": { - "type": "string" + "type": "string", + "format": "uuid" }, "required": true, - "name": "conversation", + "name": "projectId", "in": "path" } ], - "responses": { - "200": { - "description": "Subscribed", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SubscribePresenceResponse" - } + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "presence": { + "type": "string", + "enum": [ + "dark", + "online_while_sending", + "online_hours" + ] + }, + "typing": { + "type": "string", + "enum": [ + "off", + "before_text", + "before_all" + ] + }, + "reads": { + "type": "string", + "enum": [ + "off", + "replied_chats", + "all_inbound" + ] + }, + "pacing": { + "type": "string", + "enum": [ + "off", + "jittered", + "conversation" + ] + }, + "onlineStart": { + "type": "integer", + "minimum": 0, + "maximum": 23 + }, + "onlineEnd": { + "type": "integer", + "minimum": 0, + "maximum": 23 + } + }, + "additionalProperties": false } } - }, - "202": { - "description": "Accepted for asynchronous processing in response to `Prefer: respond-async`; the result is not included", + } + }, + "responses": { + "200": { + "description": "Project Safe Mode ceiling updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AsyncAcceptedResponse" + "$ref": "#/components/schemas/UpdateProjectSafeModeResponse" } } } @@ -38696,6 +38624,27 @@ } } }, + "402": { + "description": "Safe Mode is part of BanSafe Lite, which is not included on this number's plan", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "permission_error", + "code": "entitlement_required", + "message": "The account needs the required entitlement for this action.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#entitlement-required" + } + } + } + }, "403": { "description": "Authenticated but not permitted (missing scope or rule violation)", "content": { @@ -38738,8 +38687,8 @@ } } }, - "429": { - "description": "Observation limit exceeded, or the session is inside the suspension window that follows", + "500": { + "description": "Internal server error", "content": { "application/json": { "schema": { @@ -38747,20 +38696,20 @@ }, "example": { "error": { - "type": "rate_limit_error", - "code": "rate_limit_exceeded", - "message": "The request limit was reached. Follow Retry-After when supplied.", + "type": "api_error", + "code": "internal_error", + "message": "An unexpected service failure occurred. Keep the request ID for support.", "param": null, "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" }, "data": null, - "docs": "https://docs.polymorfa.com/api/errors#rate-limit-exceeded" + "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } } }, - "500": { - "description": "Internal server error", + "503": { + "description": "Stored, but a connected number did not confirm the policy; retry the request", "content": { "application/json": { "schema": { @@ -38769,34 +38718,30 @@ "example": { "error": { "type": "api_error", - "code": "internal_error", - "message": "An unexpected service failure occurred. Keep the request ID for support.", + "code": "service_unavailable", + "message": "A required service is unavailable. Check completion before repeating a write.", "param": null, "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" }, "data": null, - "docs": "https://docs.polymorfa.com/api/errors#internal-error" + "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" } } } } }, "x-mint": { - "content": "Linked Device API presence:observe" - }, - "x-protocols": [ - "Linked Device API" - ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `presence:observe`" + "content": "sessions:manage" + } } }, - "/messaging/projects/{projectId}/safe-mode": { + "/messaging/{session}/safe-mode": { "get": { - "operationId": "getProjectSafeMode", + "operationId": "getSessionSafeMode", "tags": [ "Safe Mode" ], - "summary": "Get a project's Safe Mode ceiling", + "summary": "Get a number's effective Safe Mode", "security": [ { "BearerAuth": [] @@ -38807,20 +38752,20 @@ { "schema": { "type": "string", - "format": "uuid" + "minLength": 1 }, "required": true, - "name": "projectId", + "name": "session", "in": "path" } ], "responses": { "200": { - "description": "Project Safe Mode ceiling, and whether the plan includes BanSafe Lite", + "description": "Project ceiling, number override, the effective policy, and whether the plan includes BanSafe Lite", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetProjectSafeModeResponse" + "$ref": "#/components/schemas/GetSessionSafeModeResponse" } } } @@ -38932,17 +38877,20 @@ } }, "x-mint": { - "content": "sessions:read" + "content": "Linked Device API sessions:read" }, - "description": "**Required scope:** `sessions:read`" + "x-protocols": [ + "Linked Device API" + ], + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `sessions:read`" }, "put": { - "operationId": "updateProjectSafeMode", + "operationId": "updateSessionSafeMode", "tags": [ "Safe Mode" ], - "summary": "Set a project's Safe Mode ceiling", - "description": "**Required scope:** `sessions:manage`\n\nSets the most visible behaviour any number in this project may use. Presence and read receipts are visible to the people this number messages: turning them on makes the number appear online, exposes last-seen subject to the account's privacy settings, and puts blue ticks on incoming messages. Every axis is off by default and only this request changes it.", + "summary": "Set a number's Safe Mode override", + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `sessions:manage`\n\nSets one number's Safe Mode. Each axis accepts `inherit` to follow the project ceiling. An override may only be equally or more conservative than the ceiling; a more visible value is refused with `safe_mode_exceeds_project_ceiling`.", "security": [ { "BearerAuth": [] @@ -38953,10 +38901,10 @@ { "schema": { "type": "string", - "format": "uuid" + "minLength": 1 }, "required": true, - "name": "projectId", + "name": "session", "in": "path" } ], @@ -38969,6 +38917,7 @@ "presence": { "type": "string", "enum": [ + "inherit", "dark", "online_while_sending", "online_hours" @@ -38977,6 +38926,7 @@ "typing": { "type": "string", "enum": [ + "inherit", "off", "before_text", "before_all" @@ -38985,6 +38935,7 @@ "reads": { "type": "string", "enum": [ + "inherit", "off", "replied_chats", "all_inbound" @@ -38993,20 +38944,11 @@ "pacing": { "type": "string", "enum": [ + "inherit", "off", "jittered", "conversation" ] - }, - "onlineStart": { - "type": "integer", - "minimum": 0, - "maximum": 23 - }, - "onlineEnd": { - "type": "integer", - "minimum": 0, - "maximum": 23 } }, "additionalProperties": false @@ -39016,11 +38958,11 @@ }, "responses": { "200": { - "description": "Project Safe Mode ceiling updated", + "description": "Number Safe Mode override updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateProjectSafeModeResponse" + "$ref": "#/components/schemas/UpdateSessionSafeModeResponse" } } } @@ -39174,17 +39116,20 @@ } }, "x-mint": { - "content": "sessions:manage" - } + "content": "Linked Device API sessions:manage" + }, + "x-protocols": [ + "Linked Device API" + ] } }, - "/messaging/{session}/safe-mode": { + "/messaging/projects/{projectId}/warmup-plan": { "get": { - "operationId": "getSessionSafeMode", + "operationId": "getProjectWarmupPlan", "tags": [ - "Safe Mode" + "Warm-up plan" ], - "summary": "Get a number's effective Safe Mode", + "summary": "Get a project's warm-up plan", "security": [ { "BearerAuth": [] @@ -39195,20 +39140,20 @@ { "schema": { "type": "string", - "minLength": 1 + "format": "uuid" }, "required": true, - "name": "session", + "name": "projectId", "in": "path" } ], "responses": { "200": { - "description": "Project ceiling, number override, the effective policy, and whether the plan includes BanSafe Lite", + "description": "Project warm-up plan, its projected curve, and whether the plan includes BanSafe Lite", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetSessionSafeModeResponse" + "$ref": "#/components/schemas/GetProjectWarmupPlanResponse" } } } @@ -39320,20 +39265,17 @@ } }, "x-mint": { - "content": "Linked Device API sessions:read" + "content": "sessions:read" }, - "x-protocols": [ - "Linked Device API" - ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `sessions:read`" + "description": "**Required scope:** `sessions:read`" }, "put": { - "operationId": "updateSessionSafeMode", + "operationId": "updateProjectWarmupPlan", "tags": [ - "Safe Mode" + "Warm-up plan" ], - "summary": "Set a number's Safe Mode override", - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `sessions:manage`\n\nSets one number's Safe Mode. Each axis accepts `inherit` to follow the project ceiling. An override may only be equally or more conservative than the ceiling; a more visible value is refused with `safe_mode_exceeds_project_ceiling`.", + "summary": "Set a project's warm-up plan", + "description": "**Required scope:** `sessions:manage`\n\nTurns the warm-up plan on or off for a project and sets how long the ramp takes and how many messages a number may send on its first day. While the plan is on, a send past a number's allowance is refused with `bansafe_daily_allowance_reached` and campaign recipients are deferred to the next reset.", "security": [ { "BearerAuth": [] @@ -39344,10 +39286,10 @@ { "schema": { "type": "string", - "minLength": 1 + "format": "uuid" }, "required": true, - "name": "session", + "name": "projectId", "in": "path" } ], @@ -39357,41 +39299,18 @@ "schema": { "type": "object", "properties": { - "presence": { - "type": "string", - "enum": [ - "inherit", - "dark", - "online_while_sending", - "online_hours" - ] - }, - "typing": { - "type": "string", - "enum": [ - "inherit", - "off", - "before_text", - "before_all" - ] + "enabled": { + "type": "boolean" }, - "reads": { - "type": "string", - "enum": [ - "inherit", - "off", - "replied_chats", - "all_inbound" - ] + "warmupDays": { + "type": "integer", + "minimum": 1, + "maximum": 90 }, - "pacing": { - "type": "string", - "enum": [ - "inherit", - "off", - "jittered", - "conversation" - ] + "dailyStart": { + "type": "integer", + "minimum": 1, + "maximum": 2000 } }, "additionalProperties": false @@ -39401,11 +39320,11 @@ }, "responses": { "200": { - "description": "Number Safe Mode override updated", + "description": "Project warm-up plan updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateSessionSafeModeResponse" + "$ref": "#/components/schemas/UpdateProjectWarmupPlanResponse" } } } @@ -39453,7 +39372,7 @@ } }, "402": { - "description": "Safe Mode is part of BanSafe Lite, which is not included on this number's plan", + "description": "The warm-up plan is part of BanSafe Lite, which is not included on this project's plan", "content": { "application/json": { "schema": { @@ -39537,7 +39456,7 @@ } }, "503": { - "description": "Stored, but a connected number did not confirm the policy; retry the request", + "description": "Stored, but a connected number did not confirm the plan; retry the request", "content": { "application/json": { "schema": { @@ -39559,20 +39478,17 @@ } }, "x-mint": { - "content": "Linked Device API sessions:manage" - }, - "x-protocols": [ - "Linked Device API" - ] + "content": "sessions:manage" + } } }, - "/messaging/projects/{projectId}/warmup-plan": { + "/messaging/projects/{projectId}/health-policy": { "get": { - "operationId": "getProjectWarmupPlan", + "operationId": "getProjectHealthPolicy", "tags": [ - "Warm-up plan" + "BanSafe" ], - "summary": "Get a project's warm-up plan", + "summary": "Get a project's health policy", "security": [ { "BearerAuth": [] @@ -39592,11 +39508,11 @@ ], "responses": { "200": { - "description": "Project warm-up plan, its projected curve, and whether the plan includes BanSafe Lite", + "description": "Project health threshold and configured actions", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetProjectWarmupPlanResponse" + "$ref": "#/components/schemas/GetProjectHealthPolicyResponse" } } } @@ -39708,23 +39624,201 @@ } }, "x-mint": { - "content": "sessions:read" - }, - "description": "**Required scope:** `sessions:read`" - }, - "put": { - "operationId": "updateProjectWarmupPlan", + "content": "sessions:read" + }, + "description": "**Required scope:** `sessions:read`" + }, + "put": { + "operationId": "updateProjectHealthPolicy", + "tags": [ + "BanSafe" + ], + "summary": "Set a project's health policy", + "description": "**Required scope:** `sessions:manage`\n\nReplaces the project policy when version matches the stored version. The rule triggers only when an available health estimate is below threshold. Stopping disconnects a session while retaining its linked credentials; logging out requires relinking.", + "security": [ + { + "BearerAuth": [] + } + ], + "x-required-scope": "sessions:manage", + "parameters": [ + { + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true, + "name": "projectId", + "in": "path" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateProjectHealthPolicyInput" + } + } + } + }, + "responses": { + "200": { + "description": "Project health policy updated", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateProjectHealthPolicyResponse" + } + } + } + }, + "400": { + "description": "Bad request — request body or params failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "invalid_parameter", + "message": "Correct the invalid request field.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" + } + } + } + }, + "401": { + "description": "Missing or invalid authentication token", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "authentication_error", + "code": "invalid_credential", + "message": "Supply a valid credential.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" + } + } + } + }, + "403": { + "description": "Authenticated but not permitted (missing scope or rule violation)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "permission_error", + "code": "permission_denied", + "message": "The credential does not have access to this resource.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#permission-denied" + } + } + } + }, + "404": { + "description": "Resource not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "resource_not_found", + "message": "The resource is unavailable to this caller.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" + } + } + } + }, + "409": { + "description": "Version conflict or selected notification integration unavailable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "conflict_error", + "code": "state_conflict", + "message": "The request conflicts with the resource state.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#state-conflict" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "internal_error", + "message": "An unexpected service failure occurred. Keep the request ID for support.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#internal-error" + } + } + } + } + }, + "x-mint": { + "content": "sessions:manage" + } + } + }, + "/messaging/projects/{projectId}/insurance-evidence": { + "get": { + "operationId": "getProjectInsuranceEvidence", "tags": [ - "Warm-up plan" + "Ban insurance" ], - "summary": "Set a project's warm-up plan", - "description": "**Required scope:** `sessions:manage`\n\nTurns the warm-up plan on or off for a project and sets how long the ramp takes and how many messages a number may send on its first day. While the plan is on, a send past a number's allowance is refused with `bansafe_daily_allowance_reached` and campaign recipients are deferred to the next reset.", + "summary": "Get ban insurance evidence setting", + "description": "**Required scope:** `sessions:read`\n\nBan insurance evidence lets Polymorfa measure what the other devices linked to a number do with it: how many messages they send, to how many people, how fast, and in which hours. Only counts are kept, never messages, contacts, or device names. With it off, a claim can still be filed, but Polymorfa cannot say whether another device caused the ban and a person decides the claim.", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "sessions:manage", + "x-required-scope": "sessions:read", "parameters": [ { "schema": { @@ -39736,38 +39830,13 @@ "in": "path" } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - }, - "warmupDays": { - "type": "integer", - "minimum": 1, - "maximum": 90 - }, - "dailyStart": { - "type": "integer", - "minimum": 1, - "maximum": 2000 - } - }, - "additionalProperties": false - } - } - } - }, "responses": { "200": { - "description": "Project warm-up plan updated", + "description": "The project's ban insurance evidence setting", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateProjectWarmupPlanResponse" + "$ref": "#/components/schemas/GetProjectInsuranceEvidenceResponse" } } } @@ -39814,27 +39883,6 @@ } } }, - "402": { - "description": "The warm-up plan is part of BanSafe Lite, which is not included on this project's plan", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "permission_error", - "code": "entitlement_required", - "message": "The account needs the required entitlement for this action.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#entitlement-required" - } - } - } - }, "403": { "description": "Authenticated but not permitted (missing scope or rule violation)", "content": { @@ -39897,47 +39945,25 @@ } } } - }, - "503": { - "description": "Stored, but a connected number did not confirm the plan; retry the request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "api_error", - "code": "service_unavailable", - "message": "A required service is unavailable. Check completion before repeating a write.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" - } - } - } } }, "x-mint": { - "content": "sessions:manage" + "content": "sessions:read" } - } - }, - "/messaging/projects/{projectId}/health-policy": { - "get": { - "operationId": "getProjectHealthPolicy", + }, + "put": { + "operationId": "updateProjectInsuranceEvidence", "tags": [ - "BanSafe" + "Ban insurance" ], - "summary": "Get a project's health policy", + "summary": "Set ban insurance evidence setting", + "description": "**Required scope:** `sessions:manage`\n\nBan insurance evidence lets Polymorfa measure what the other devices linked to a number do with it: how many messages they send, to how many people, how fast, and in which hours. Only counts are kept, never messages, contacts, or device names. With it off, a claim can still be filed, but Polymorfa cannot say whether another device caused the ban and a person decides the claim.", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "sessions:read", + "x-required-scope": "sessions:manage", "parameters": [ { "schema": { @@ -39949,13 +39975,31 @@ "in": "path" } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + }, + "required": [ + "enabled" + ], + "additionalProperties": false + } + } + } + }, "responses": { "200": { - "description": "Project health threshold and configured actions", + "description": "The project's ban insurance evidence setting was updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetProjectHealthPolicyResponse" + "$ref": "#/components/schemas/UpdateProjectInsuranceEvidenceResponse" } } } @@ -40064,26 +40108,48 @@ } } } + }, + "503": { + "description": "Stored, but a connected number did not confirm the change; retry the request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "service_unavailable", + "message": "A required service is unavailable. Check completion before repeating a write.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" + } + } + } } }, "x-mint": { - "content": "sessions:read" - }, - "description": "**Required scope:** `sessions:read`" - }, - "put": { - "operationId": "updateProjectHealthPolicy", + "content": "sessions:manage" + } + } + }, + "/messaging/projects/{projectId}/observation-policy": { + "get": { + "operationId": "getProjectObservationPolicy", "tags": [ - "BanSafe" + "Presence", + "Labels", + "Business App" ], - "summary": "Set a project's health policy", - "description": "**Required scope:** `sessions:manage`\n\nReplaces the project policy when version matches the stored version. The rule triggers only when an available health estimate is below threshold. Stopping disconnects a session while retaining its linked credentials; logging out requires relinking.", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "sessions:manage", + "x-required-scope": "presence:read", "parameters": [ { "schema": { @@ -40095,23 +40161,13 @@ "in": "path" } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateProjectHealthPolicyInput" - } - } - } - }, "responses": { "200": { - "description": "Project health policy updated", + "description": "Project observation ceiling", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateProjectHealthPolicyResponse" + "$ref": "#/components/schemas/GetProjectObservationPolicyResponse" } } } @@ -40200,27 +40256,6 @@ } } }, - "409": { - "description": "Version conflict or selected notification integration unavailable", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "conflict_error", - "code": "state_conflict", - "message": "The request conflicts with the resource state.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#state-conflict" - } - } - } - }, "500": { "description": "Internal server error", "content": { @@ -40244,42 +40279,43 @@ } }, "x-mint": { - "content": "sessions:manage" - } + "content": "presence:read" + }, + "description": "**Required scope:** `presence:read`" } }, - "/messaging/projects/{projectId}/insurance-evidence": { + "/messaging/{session}/observation-policy": { "get": { - "operationId": "getProjectInsuranceEvidence", + "operationId": "getSessionObservationPolicy", "tags": [ - "Ban insurance" + "Presence", + "Labels", + "Business App" ], - "summary": "Get ban insurance evidence setting", - "description": "**Required scope:** `sessions:read`\n\nBan insurance evidence lets Polymorfa measure what the other devices linked to a number do with it: how many messages they send, to how many people, how fast, and in which hours. Only counts are kept, never messages, contacts, or device names. With it off, a claim can still be filed, but Polymorfa cannot say whether another device caused the ban and a person decides the claim.", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "sessions:read", + "x-required-scope": "presence:read", "parameters": [ { "schema": { "type": "string", - "format": "uuid" + "minLength": 1 }, "required": true, - "name": "projectId", + "name": "session", "in": "path" } ], "responses": { "200": { - "description": "The project's ban insurance evidence setting", + "description": "Effective session observation policy", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetProjectInsuranceEvidenceResponse" + "$ref": "#/components/schemas/GetSessionObservationPolicyResponse" } } } @@ -40391,58 +40427,44 @@ } }, "x-mint": { - "content": "sessions:read" - } - }, - "put": { - "operationId": "updateProjectInsuranceEvidence", + "content": "Linked Device API presence:read" + }, + "x-protocols": [ + "Linked Device API" + ], + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `presence:read`" + } + }, + "/messaging/{session}/profile": { + "get": { + "operationId": "getProfile", "tags": [ - "Ban insurance" + "Profile" ], - "summary": "Set ban insurance evidence setting", - "description": "**Required scope:** `sessions:manage`\n\nBan insurance evidence lets Polymorfa measure what the other devices linked to a number do with it: how many messages they send, to how many people, how fast, and in which hours. Only counts are kept, never messages, contacts, or device names. With it off, a claim can still be filed, but Polymorfa cannot say whether another device caused the ban and a person decides the claim.", + "summary": "Get the session's own profile", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "sessions:manage", + "x-required-scope": "profile:read", "parameters": [ { "schema": { - "type": "string", - "format": "uuid" + "type": "string" }, "required": true, - "name": "projectId", + "name": "session", "in": "path" } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "enabled": { - "type": "boolean" - } - }, - "required": [ - "enabled" - ], - "additionalProperties": false - } - } - } - }, "responses": { "200": { - "description": "The project's ban insurance evidence setting was updated", + "description": "Profile info", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateProjectInsuranceEvidenceResponse" + "$ref": "#/components/schemas/GetProfileResponse" } } } @@ -40551,66 +40573,56 @@ } } } - }, - "503": { - "description": "Stored, but a connected number did not confirm the change; retry the request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "api_error", - "code": "service_unavailable", - "message": "A required service is unavailable. Check completion before repeating a write.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" - } - } - } } }, "x-mint": { - "content": "sessions:manage" - } + "content": "Linked Device API profile:read" + }, + "x-protocols": [ + "Linked Device API" + ], + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:read`" } }, - "/messaging/projects/{projectId}/observation-policy": { - "get": { - "operationId": "getProjectObservationPolicy", + "/messaging/{session}/profile/name": { + "put": { + "operationId": "setProfileName", "tags": [ - "Presence", - "Labels", - "Business App" + "Profile" ], + "summary": "Set profile display name (push name)", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "presence:read", + "x-required-scope": "profile:write", "parameters": [ { "schema": { - "type": "string", - "format": "uuid" + "type": "string" }, "required": true, - "name": "projectId", + "name": "session", "in": "path" } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SetProfileNameRequest" + } + } + } + }, "responses": { "200": { - "description": "Project observation ceiling", + "description": "Name updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetProjectObservationPolicyResponse" + "$ref": "#/components/schemas/SuccessResponse" } } } @@ -40722,43 +40734,53 @@ } }, "x-mint": { - "content": "presence:read" + "content": "Linked Device API profile:write" }, - "description": "**Required scope:** `presence:read`" + "x-protocols": [ + "Linked Device API" + ], + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" } }, - "/messaging/{session}/observation-policy": { - "get": { - "operationId": "getSessionObservationPolicy", + "/messaging/{session}/profile/status": { + "put": { + "operationId": "setProfileStatus", "tags": [ - "Presence", - "Labels", - "Business App" + "Profile" ], + "summary": "Set profile status (about) text", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "presence:read", + "x-required-scope": "profile:write", "parameters": [ { "schema": { - "type": "string", - "minLength": 1 + "type": "string" }, "required": true, "name": "session", "in": "path" } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SetProfileStatusRequest" + } + } + } + }, "responses": { "200": { - "description": "Effective session observation policy", + "description": "Status updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetSessionObservationPolicyResponse" + "$ref": "#/components/schemas/SuccessResponse" } } } @@ -40870,27 +40892,27 @@ } }, "x-mint": { - "content": "Linked Device API presence:read" + "content": "Linked Device API profile:write" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `presence:read`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" } }, - "/messaging/{session}/profile": { - "get": { - "operationId": "getProfile", + "/messaging/{session}/profile/picture": { + "put": { + "operationId": "setProfilePicture", "tags": [ "Profile" ], - "summary": "Get the session's own profile", + "summary": "Set profile picture", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "profile:read", + "x-required-scope": "profile:write", "parameters": [ { "schema": { @@ -40901,13 +40923,22 @@ "in": "path" } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SetProfilePictureRequest" + } + } + } + }, "responses": { "200": { - "description": "Profile info", + "description": "Picture updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetProfileResponse" + "$ref": "#/components/schemas/SuccessResponse" } } } @@ -41019,21 +41050,19 @@ } }, "x-mint": { - "content": "Linked Device API profile:read" + "content": "Linked Device API profile:write" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:read`" - } - }, - "/messaging/{session}/profile/name": { - "put": { - "operationId": "setProfileName", + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" + }, + "delete": { + "operationId": "deleteProfilePicture", "tags": [ "Profile" ], - "summary": "Set profile display name (push name)", + "summary": "Remove the profile picture", "security": [ { "BearerAuth": [] @@ -41050,18 +41079,9 @@ "in": "path" } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SetProfileNameRequest" - } - } - } - }, "responses": { "200": { - "description": "Name updated", + "description": "Picture deleted", "content": { "application/json": { "schema": { @@ -41185,19 +41205,19 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" } }, - "/messaging/{session}/profile/status": { - "put": { - "operationId": "setProfileStatus", + "/messaging/{session}/privacy": { + "get": { + "operationId": "getPrivacySettings", "tags": [ - "Profile" + "Privacy" ], - "summary": "Set profile status (about) text", + "summary": "Get privacy settings", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "profile:write", + "x-required-scope": "profile:read", "parameters": [ { "schema": { @@ -41208,22 +41228,13 @@ "in": "path" } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SetProfileStatusRequest" - } - } - } - }, "responses": { "200": { - "description": "Status updated", + "description": "Privacy settings", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/PrivacySettingsResponse" } } } @@ -41335,21 +41346,21 @@ } }, "x-mint": { - "content": "Linked Device API profile:write" + "content": "Linked Device API profile:read" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:read`" } }, - "/messaging/{session}/profile/picture": { + "/messaging/{session}/privacy/disappearing/default": { "put": { - "operationId": "setProfilePicture", + "operationId": "setDefaultDisappearingTimer", "tags": [ - "Profile" + "Privacy" ], - "summary": "Set profile picture", + "summary": "Set the default disappearing-message timer", "security": [ { "BearerAuth": [] @@ -41370,14 +41381,14 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SetProfilePictureRequest" + "$ref": "#/components/schemas/DefaultDisappearingTimerRequest" } } } }, "responses": { "200": { - "description": "Picture updated", + "description": "Default disappearing-message timer updated", "content": { "application/json": { "schema": { @@ -41499,13 +41510,15 @@ "Linked Device API" ], "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" - }, - "delete": { - "operationId": "deleteProfilePicture", + } + }, + "/messaging/{session}/privacy/{setting}": { + "put": { + "operationId": "setPrivacySetting", "tags": [ - "Profile" + "Privacy" ], - "summary": "Remove the profile picture", + "summary": "Update a privacy setting", "security": [ { "BearerAuth": [] @@ -41520,15 +41533,44 @@ "required": true, "name": "session", "in": "path" + }, + { + "schema": { + "type": "string", + "enum": [ + "groupadd", + "last", + "status", + "profile", + "readreceipts", + "online", + "calladd", + "messages", + "defense", + "stickers" + ] + }, + "required": true, + "name": "setting", + "in": "path" } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SetPrivacySettingRequest" + } + } + } + }, "responses": { "200": { - "description": "Picture deleted", + "description": "Updated privacy settings", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/UpdatedPrivacySettingsResponse" } } } @@ -41648,19 +41690,19 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" } }, - "/messaging/{session}/privacy": { + "/messaging/{session}/labels": { "get": { - "operationId": "getPrivacySettings", + "operationId": "listLabels", "tags": [ - "Privacy" + "Labels" ], - "summary": "Get privacy settings", + "summary": "List labels defined for the session", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "profile:read", + "x-required-scope": "labels:read", "parameters": [ { "schema": { @@ -41669,15 +41711,27 @@ "required": true, "name": "session", "in": "path" + }, + { + "schema": { + "type": "string", + "enum": [ + "true", + "false" + ] + }, + "required": false, + "name": "includeObservation", + "in": "query" } ], "responses": { "200": { - "description": "Privacy settings", + "description": "Label list", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PrivacySettingsResponse" + "$ref": "#/components/schemas/ListLabelsResponse" } } } @@ -41789,27 +41843,25 @@ } }, "x-mint": { - "content": "Linked Device API profile:read" + "content": "Linked Device API labels:read" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:read`" - } - }, - "/messaging/{session}/privacy/disappearing/default": { - "put": { - "operationId": "setDefaultDisappearingTimer", + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `labels:read`" + }, + "post": { + "operationId": "createLabel", "tags": [ - "Privacy" + "Labels" ], - "summary": "Set the default disappearing-message timer", + "summary": "Create a label", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "profile:write", + "x-required-scope": "labels:manage", "parameters": [ { "schema": { @@ -41824,18 +41876,18 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DefaultDisappearingTimerRequest" + "$ref": "#/components/schemas/CreateLabelRequest" } } } }, "responses": { "200": { - "description": "Default disappearing-message timer updated", + "description": "Label created", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/CreateLabelResponse" } } } @@ -41947,27 +41999,27 @@ } }, "x-mint": { - "content": "Linked Device API profile:write" + "content": "Linked Device API labels:manage" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `labels:manage`" } }, - "/messaging/{session}/privacy/{setting}": { + "/messaging/{session}/labels/{labelId}": { "put": { - "operationId": "setPrivacySetting", + "operationId": "updateLabel", "tags": [ - "Privacy" + "Labels" ], - "summary": "Update a privacy setting", + "summary": "Update a label", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "profile:write", + "x-required-scope": "labels:manage", "parameters": [ { "schema": { @@ -41979,22 +42031,10 @@ }, { "schema": { - "type": "string", - "enum": [ - "groupadd", - "last", - "status", - "profile", - "readreceipts", - "online", - "calladd", - "messages", - "defense", - "stickers" - ] + "type": "string" }, "required": true, - "name": "setting", + "name": "labelId", "in": "path" } ], @@ -42002,18 +42042,18 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SetPrivacySettingRequest" + "$ref": "#/components/schemas/UpdateLabelRequest" } } } }, "responses": { "200": { - "description": "Updated privacy settings", + "description": "Label updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdatedPrivacySettingsResponse" + "$ref": "#/components/schemas/SuccessResponse" } } } @@ -42102,148 +42142,8 @@ } } }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "api_error", - "code": "internal_error", - "message": "An unexpected service failure occurred. Keep the request ID for support.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#internal-error" - } - } - } - } - }, - "x-mint": { - "content": "Linked Device API profile:write" - }, - "x-protocols": [ - "Linked Device API" - ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" - } - }, - "/messaging/{session}/labels": { - "get": { - "operationId": "listLabels", - "tags": [ - "Labels" - ], - "summary": "List labels defined for the session", - "security": [ - { - "BearerAuth": [] - } - ], - "x-required-scope": "labels:read", - "parameters": [ - { - "schema": { - "type": "string" - }, - "required": true, - "name": "session", - "in": "path" - }, - { - "schema": { - "type": "string", - "enum": [ - "true", - "false" - ] - }, - "required": false, - "name": "includeObservation", - "in": "query" - } - ], - "responses": { - "200": { - "description": "Label list", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ListLabelsResponse" - } - } - } - }, - "400": { - "description": "Bad request — request body or params failed validation", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "invalid_request_error", - "code": "invalid_parameter", - "message": "Correct the invalid request field.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" - } - } - } - }, - "401": { - "description": "Missing or invalid authentication token", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "authentication_error", - "code": "invalid_credential", - "message": "Supply a valid credential.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" - } - } - } - }, - "403": { - "description": "Authenticated but not permitted (missing scope or rule violation)", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "permission_error", - "code": "permission_denied", - "message": "The credential does not have access to this resource.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#permission-denied" - } - } - } - }, - "404": { - "description": "Resource not found", + "409": { + "description": "The session has no authoritative view of its labels yet. Retry once a cached or project-retained snapshot is complete.", "content": { "application/json": { "schema": { @@ -42251,14 +42151,14 @@ }, "example": { "error": { - "type": "invalid_request_error", - "code": "resource_not_found", - "message": "The resource is unavailable to this caller.", + "type": "conflict_error", + "code": "state_conflict", + "message": "The request conflicts with the resource state.", "param": null, "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" }, "data": null, - "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" + "docs": "https://docs.polymorfa.com/api/errors#state-conflict" } } } @@ -42286,19 +42186,19 @@ } }, "x-mint": { - "content": "Linked Device API labels:read" + "content": "Linked Device API labels:manage" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `labels:read`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `labels:manage`" }, - "post": { - "operationId": "createLabel", + "delete": { + "operationId": "deleteLabel", "tags": [ "Labels" ], - "summary": "Create a label", + "summary": "Delete a label", "security": [ { "BearerAuth": [] @@ -42313,24 +42213,23 @@ "required": true, "name": "session", "in": "path" + }, + { + "schema": { + "type": "string" + }, + "required": true, + "name": "labelId", + "in": "path" } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateLabelRequest" - } - } - } - }, "responses": { "200": { - "description": "Label created", + "description": "Label deleted", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateLabelResponse" + "$ref": "#/components/schemas/SuccessResponse" } } } @@ -42450,19 +42349,19 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `labels:manage`" } }, - "/messaging/{session}/labels/{labelId}": { - "put": { - "operationId": "updateLabel", + "/messaging/{session}/labels/chats/{conversation}": { + "get": { + "operationId": "getChatLabels", "tags": [ "Labels" ], - "summary": "Update a label", + "summary": "Get labels attached to a chat", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "labels:manage", + "x-required-scope": "labels:read", "parameters": [ { "schema": { @@ -42477,26 +42376,29 @@ "type": "string" }, "required": true, - "name": "labelId", + "name": "conversation", "in": "path" + }, + { + "schema": { + "type": "string", + "enum": [ + "true", + "false" + ] + }, + "required": false, + "name": "includeObservation", + "in": "query" } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateLabelRequest" - } - } - } - }, "responses": { "200": { - "description": "Label updated", + "description": "Chat labels", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/GetChatLabelsResponse" } } } @@ -42585,27 +42487,6 @@ } } }, - "409": { - "description": "The session has no authoritative view of its labels yet. Retry once a cached or project-retained snapshot is complete.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "conflict_error", - "code": "state_conflict", - "message": "The request conflicts with the resource state.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#state-conflict" - } - } - } - }, "500": { "description": "Internal server error", "content": { @@ -42629,19 +42510,19 @@ } }, "x-mint": { - "content": "Linked Device API labels:manage" + "content": "Linked Device API labels:read" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `labels:manage`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `labels:read`" }, - "delete": { - "operationId": "deleteLabel", + "put": { + "operationId": "setChatLabels", "tags": [ "Labels" ], - "summary": "Delete a label", + "summary": "Replace the set of labels attached to a chat", "security": [ { "BearerAuth": [] @@ -42662,13 +42543,22 @@ "type": "string" }, "required": true, - "name": "labelId", + "name": "conversation", "in": "path" } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SetChatLabelsRequest" + } + } + } + }, "responses": { "200": { - "description": "Label deleted", + "description": "Chat labels updated", "content": { "application/json": { "schema": { @@ -42761,6 +42651,27 @@ } } }, + "409": { + "description": "The session has no authoritative view of its labels yet. Retry once a cached or project-retained snapshot is complete.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "conflict_error", + "code": "state_conflict", + "message": "The request conflicts with the resource state.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#state-conflict" + } + } + } + }, "500": { "description": "Internal server error", "content": { @@ -42792,56 +42703,26 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `labels:manage`" } }, - "/messaging/{session}/labels/chats/{conversation}": { + "/messaging/webhooks": { "get": { - "operationId": "getChatLabels", + "operationId": "listWebhooks", "tags": [ - "Labels" + "Webhooks" ], - "summary": "Get labels attached to a chat", + "summary": "List webhooks for the tenant", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "labels:read", - "parameters": [ - { - "schema": { - "type": "string" - }, - "required": true, - "name": "session", - "in": "path" - }, - { - "schema": { - "type": "string" - }, - "required": true, - "name": "conversation", - "in": "path" - }, - { - "schema": { - "type": "string", - "enum": [ - "true", - "false" - ] - }, - "required": false, - "name": "includeObservation", - "in": "query" - } - ], + "x-required-scope": "webhooks:manage", "responses": { "200": { - "description": "Chat labels", + "description": "Webhook list", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetChatLabelsResponse" + "$ref": "#/components/schemas/ListWebhooksResponse" } } } @@ -42953,59 +42834,38 @@ } }, "x-mint": { - "content": "Linked Device API labels:read" + "content": "webhooks:manage" }, - "x-protocols": [ - "Linked Device API" - ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `labels:read`" + "description": "**Required scope:** `webhooks:manage`" }, - "put": { - "operationId": "setChatLabels", + "post": { + "operationId": "createWebhook", "tags": [ - "Labels" + "Webhooks" ], - "summary": "Replace the set of labels attached to a chat", + "summary": "Register a new webhook", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "labels:manage", - "parameters": [ - { - "schema": { - "type": "string" - }, - "required": true, - "name": "session", - "in": "path" - }, - { - "schema": { - "type": "string" - }, - "required": true, - "name": "conversation", - "in": "path" - } - ], + "x-required-scope": "webhooks:manage", "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SetChatLabelsRequest" + "$ref": "#/components/schemas/CreateWebhookRequest" } } } }, "responses": { - "200": { - "description": "Chat labels updated", + "201": { + "description": "Webhook created", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/CreateWebhookResponse" } } } @@ -43094,27 +42954,6 @@ } } }, - "409": { - "description": "The session has no authoritative view of its labels yet. Retry once a cached or project-retained snapshot is complete.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "conflict_error", - "code": "state_conflict", - "message": "The request conflicts with the resource state.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#state-conflict" - } - } - } - }, "500": { "description": "Internal server error", "content": { @@ -43138,34 +42977,41 @@ } }, "x-mint": { - "content": "Linked Device API labels:manage" + "content": "webhooks:manage" }, - "x-protocols": [ - "Linked Device API" - ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `labels:manage`" + "description": "**Required scope:** `webhooks:manage`" } }, - "/messaging/webhooks": { + "/messaging/webhooks/{id}": { "get": { - "operationId": "listWebhooks", + "operationId": "getWebhook", "tags": [ "Webhooks" ], - "summary": "List webhooks for the tenant", + "summary": "Get a webhook by id", "security": [ { "BearerAuth": [] } ], "x-required-scope": "webhooks:manage", + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "id", + "in": "path" + } + ], "responses": { "200": { - "description": "Webhook list", + "description": "Webhook details", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ListWebhooksResponse" + "$ref": "#/components/schemas/GetWebhookResponse" } } } @@ -43281,34 +43127,44 @@ }, "description": "**Required scope:** `webhooks:manage`" }, - "post": { - "operationId": "createWebhook", + "put": { + "operationId": "updateWebhook", "tags": [ "Webhooks" ], - "summary": "Register a new webhook", + "summary": "Update a webhook", "security": [ { "BearerAuth": [] } ], "x-required-scope": "webhooks:manage", + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "id", + "in": "path" + } + ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateWebhookRequest" + "$ref": "#/components/schemas/UpdateWebhookRequest" } } } }, "responses": { - "201": { - "description": "Webhook created", + "200": { + "description": "Webhook updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateWebhookResponse" + "$ref": "#/components/schemas/UpdateWebhookResponse" } } } @@ -43423,15 +43279,13 @@ "content": "webhooks:manage" }, "description": "**Required scope:** `webhooks:manage`" - } - }, - "/messaging/webhooks/{id}": { - "get": { - "operationId": "getWebhook", + }, + "delete": { + "operationId": "deleteWebhook", "tags": [ "Webhooks" ], - "summary": "Get a webhook by id", + "summary": "Delete a webhook", "security": [ { "BearerAuth": [] @@ -43450,11 +43304,11 @@ ], "responses": { "200": { - "description": "Webhook details", + "description": "Webhook deleted", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetWebhookResponse" + "$ref": "#/components/schemas/SuccessResponse" } } } @@ -43569,19 +43423,21 @@ "content": "webhooks:manage" }, "description": "**Required scope:** `webhooks:manage`" - }, - "put": { - "operationId": "updateWebhook", + } + }, + "/messaging/media/{id}": { + "get": { + "operationId": "downloadMedia", "tags": [ - "Webhooks" + "Media" ], - "summary": "Update a webhook", + "summary": "Download media (binary stream or redirect to S3)", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "webhooks:manage", + "x-required-scope": "media:read", "parameters": [ { "schema": { @@ -43592,25 +43448,12 @@ "in": "path" } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateWebhookRequest" - } - } - } - }, "responses": { "200": { - "description": "Webhook updated", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateWebhookResponse" - } - } - } + "description": "Media binary stream" + }, + "302": { + "description": "Redirect to S3" }, "400": { "description": "Bad request — request body or params failed validation", @@ -43719,22 +43562,27 @@ } }, "x-mint": { - "content": "webhooks:manage" + "content": "Linked Device API media:read" }, - "description": "**Required scope:** `webhooks:manage`" - }, - "delete": { - "operationId": "deleteWebhook", + "x-protocols": [ + "Linked Device API" + ], + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `media:read`" + } + }, + "/messaging/media/{id}/info": { + "get": { + "operationId": "getMediaInfo", "tags": [ - "Webhooks" + "Media" ], - "summary": "Delete a webhook", + "summary": "Get media metadata", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "webhooks:manage", + "x-required-scope": "media:read", "parameters": [ { "schema": { @@ -43747,11 +43595,11 @@ ], "responses": { "200": { - "description": "Webhook deleted", + "description": "Media info", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/GetMediaInfoResponse" } } } @@ -43863,24 +43711,27 @@ } }, "x-mint": { - "content": "webhooks:manage" + "content": "Linked Device API media:read" }, - "description": "**Required scope:** `webhooks:manage`" + "x-protocols": [ + "Linked Device API" + ], + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `media:read`" } }, - "/messaging/media/{id}": { - "get": { - "operationId": "downloadMedia", + "/messaging/media/{id}/download-and-save": { + "post": { + "operationId": "downloadAndSaveMedia", "tags": [ "Media" ], - "summary": "Download media (binary stream or redirect to S3)", + "summary": "Download media and persist it to the tenant's S3 bucket", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "media:read", + "x-required-scope": "media:manage", "parameters": [ { "schema": { @@ -43893,10 +43744,14 @@ ], "responses": { "200": { - "description": "Media binary stream" - }, - "302": { - "description": "Redirect to S3" + "description": "Persistence queued or already persisted", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SuccessResponse" + } + } + } }, "400": { "description": "Bad request — request body or params failed validation", @@ -44005,44 +43860,44 @@ } }, "x-mint": { - "content": "Linked Device API media:read" + "content": "Linked Device API media:manage" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `media:read`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `media:manage`" } }, - "/messaging/media/{id}/info": { + "/messaging/projects/{projectSlug}/templates": { "get": { - "operationId": "getMediaInfo", + "operationId": "listProjectTemplates", "tags": [ - "Media" + "Templates" ], - "summary": "Get media metadata", + "summary": "List project message templates", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "media:read", + "x-required-scope": "templates:read", "parameters": [ { "schema": { "type": "string" }, "required": true, - "name": "id", + "name": "projectSlug", "in": "path" } ], "responses": { "200": { - "description": "Media info", + "description": "Templates", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetMediaInfoResponse" + "$ref": "#/components/schemas/ListProjectTemplatesResponse" } } } @@ -44154,44 +44009,48 @@ } }, "x-mint": { - "content": "Linked Device API media:read" + "content": "templates:read" }, - "x-protocols": [ - "Linked Device API" - ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `media:read`" - } - }, - "/messaging/media/{id}/download-and-save": { + "description": "**Required scope:** `templates:read`" + }, "post": { - "operationId": "downloadAndSaveMedia", + "operationId": "createProjectTemplate", "tags": [ - "Media" + "Templates" ], - "summary": "Download media and persist it to the tenant's S3 bucket", + "summary": "Create a project message template", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "media:manage", + "x-required-scope": "templates:manage", "parameters": [ { "schema": { "type": "string" }, "required": true, - "name": "id", + "name": "projectSlug", "in": "path" } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateMessageTemplateRequest" + } + } + } + }, "responses": { - "200": { - "description": "Persistence queued or already persisted", + "201": { + "description": "Created", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/CreateProjectTemplateResponse" } } } @@ -44303,21 +44162,18 @@ } }, "x-mint": { - "content": "Linked Device API media:manage" + "content": "templates:manage" }, - "x-protocols": [ - "Linked Device API" - ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `media:manage`" + "description": "**Required scope:** `templates:manage`" } }, - "/messaging/projects/{projectSlug}/templates": { + "/messaging/projects/{projectSlug}/templates/{id}": { "get": { - "operationId": "listProjectTemplates", + "operationId": "getProjectTemplate", "tags": [ "Templates" ], - "summary": "List project message templates", + "summary": "Get a project message template", "security": [ { "BearerAuth": [] @@ -44332,15 +44188,23 @@ "required": true, "name": "projectSlug", "in": "path" + }, + { + "schema": { + "type": "string" + }, + "required": true, + "name": "id", + "in": "path" } ], "responses": { "200": { - "description": "Templates", + "description": "Template", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ListProjectTemplatesResponse" + "$ref": "#/components/schemas/GetProjectTemplateResponse" } } } @@ -44456,12 +44320,12 @@ }, "description": "**Required scope:** `templates:read`" }, - "post": { - "operationId": "createProjectTemplate", + "patch": { + "operationId": "updateProjectTemplate", "tags": [ "Templates" ], - "summary": "Create a project message template", + "summary": "Update a project message template", "security": [ { "BearerAuth": [] @@ -44476,24 +44340,32 @@ "required": true, "name": "projectSlug", "in": "path" + }, + { + "schema": { + "type": "string" + }, + "required": true, + "name": "id", + "in": "path" } ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateMessageTemplateRequest" + "$ref": "#/components/schemas/UpdateMessageTemplateRequest" } } } }, "responses": { - "201": { - "description": "Created", + "200": { + "description": "Updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateProjectTemplateResponse" + "$ref": "#/components/schemas/UpdateProjectTemplateResponse" } } } @@ -44608,21 +44480,19 @@ "content": "templates:manage" }, "description": "**Required scope:** `templates:manage`" - } - }, - "/messaging/projects/{projectSlug}/templates/{id}": { - "get": { - "operationId": "getProjectTemplate", + }, + "delete": { + "operationId": "deleteProjectTemplate", "tags": [ "Templates" ], - "summary": "Get a project message template", + "summary": "Delete a project message template", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "templates:read", + "x-required-scope": "templates:manage", "parameters": [ { "schema": { @@ -44643,11 +44513,11 @@ ], "responses": { "200": { - "description": "Template", + "description": "Deleted", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetProjectTemplateResponse" + "$ref": "#/components/schemas/SuccessResponse" } } } @@ -44759,22 +44629,24 @@ } }, "x-mint": { - "content": "templates:read" + "content": "templates:manage" }, - "description": "**Required scope:** `templates:read`" - }, - "patch": { - "operationId": "updateProjectTemplate", + "description": "**Required scope:** `templates:manage`" + } + }, + "/messaging/projects/{projectSlug}/templates/{id}/preview": { + "post": { + "operationId": "previewProjectTemplate", "tags": [ "Templates" ], - "summary": "Update a project message template", + "summary": "Render a template preview (idealized) or surface-specific output", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "templates:manage", + "x-required-scope": "templates:read", "parameters": [ { "schema": { @@ -44797,18 +44669,18 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateMessageTemplateRequest" + "$ref": "#/components/schemas/PreviewMessageTemplateRequest" } } } }, "responses": { "200": { - "description": "Updated", + "description": "Rendered", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateProjectTemplateResponse" + "$ref": "#/components/schemas/PreviewProjectTemplateResponse" } } } @@ -44920,16 +44792,19 @@ } }, "x-mint": { - "content": "templates:manage" + "content": "templates:read" }, - "description": "**Required scope:** `templates:manage`" - }, - "delete": { - "operationId": "deleteProjectTemplate", + "description": "**Required scope:** `templates:read`" + } + }, + "/messaging/projects/{projectSlug}/templates/{id}/submit": { + "post": { + "operationId": "submitProjectTemplateToMeta", "tags": [ "Templates" ], - "summary": "Delete a project message template", + "summary": "Submit a project template to Meta for approval on a Cloud-API session", + "description": "**Required scope:** `templates:manage`\n\nRenders the canonical template into a Meta create-template payload and submits it through the given cloud_api session. Records a per-session approval pointer (cloudLinks) and returns the updated template. The Meta review verdict arrives later via webhook.", "security": [ { "BearerAuth": [] @@ -44954,13 +44829,22 @@ "in": "path" } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SubmitMessageTemplateRequest" + } + } + } + }, "responses": { "200": { - "description": "Deleted", + "description": "Submitted", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/SubmitProjectTemplateResponse" } } } @@ -45073,23 +44957,22 @@ }, "x-mint": { "content": "templates:manage" - }, - "description": "**Required scope:** `templates:manage`" + } } }, - "/messaging/projects/{projectSlug}/templates/{id}/preview": { - "post": { - "operationId": "previewProjectTemplate", + "/messaging/projects/{projectSlug}/campaigns": { + "get": { + "operationId": "listCampaigns", "tags": [ - "Templates" + "Campaigns" ], - "summary": "Render a template preview (idealized) or surface-specific output", + "summary": "List campaigns", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "templates:read", + "x-required-scope": "campaigns:read", "parameters": [ { "schema": { @@ -45098,32 +44981,15 @@ "required": true, "name": "projectSlug", "in": "path" - }, - { - "schema": { - "type": "string" - }, - "required": true, - "name": "id", - "in": "path" } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PreviewMessageTemplateRequest" - } - } - } - }, "responses": { "200": { - "description": "Rendered", + "description": "OK", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PreviewProjectTemplateResponse" + "$ref": "#/components/schemas/ListCampaignsResponse" } } } @@ -45235,25 +45101,23 @@ } }, "x-mint": { - "content": "templates:read" + "content": "campaigns:read" }, - "description": "**Required scope:** `templates:read`" - } - }, - "/messaging/projects/{projectSlug}/templates/{id}/submit": { + "description": "**Required scope:** `campaigns:read`" + }, "post": { - "operationId": "submitProjectTemplateToMeta", + "operationId": "createCampaign", "tags": [ - "Templates" + "Campaigns" ], - "summary": "Submit a project template to Meta for approval on a Cloud-API session", - "description": "**Required scope:** `templates:manage`\n\nRenders the canonical template into a Meta create-template payload and submits it through the given cloud_api session. Records a per-session approval pointer (cloudLinks) and returns the updated template. The Meta review verdict arrives later via webhook.", + "summary": "Create a campaign", + "description": "**Required scope:** `campaigns:manage`\n\nCreates a draft campaign on any plan. `senderConfig.sessionIds` may name numbers that are not yet on a plan that includes Campaigns; launch checks the selection. It is refused with `403 bansafe_org_suspended` while the team is suspended and `400` when `senderConfig.sessionIds` is malformed or a per-number cap is above its safety ceiling.", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "templates:manage", + "x-required-scope": "campaigns:manage", "parameters": [ { "schema": { @@ -45264,30 +45128,33 @@ "in": "path" }, { + "name": "Idempotency-Key", + "in": "header", + "required": false, + "description": "Unique key, 1 to 255 bytes of UTF-8, that makes this write safe to retry for 24 hours. A retry with the same key, credential, `Polymorfa-Version`, and request never runs the write again: it returns `409 idempotency_completed` when the original succeeded, or the original server error, with `Idempotent-Replayed: true`. Reusing the key for a different request returns `409 idempotency_conflict`; a retry while the original is still running returns `409 idempotency_in_progress`, and a retry after an unrecorded outcome returns `409 idempotency_outcome_unknown`.", "schema": { - "type": "string" - }, - "required": true, - "name": "id", - "in": "path" + "type": "string", + "minLength": 1, + "maxLength": 255 + } } ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SubmitMessageTemplateRequest" + "$ref": "#/components/schemas/CreateCampaignRequest" } } } }, "responses": { "200": { - "description": "Submitted", + "description": "OK", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SubmitProjectTemplateResponse" + "$ref": "#/components/schemas/CreateCampaignResponse" } } } @@ -45376,6 +45243,44 @@ } } }, + "409": { + "description": "The Idempotency-Key already succeeded (`idempotency_completed`), was used for a different request (`idempotency_conflict`), belongs to a request that is still running (`idempotency_in_progress`, with Retry-After), or belongs to a request whose outcome was not recorded (`idempotency_outcome_unknown`)", + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying `idempotency_in_progress`.", + "schema": { + "type": "integer", + "minimum": 0 + } + }, + "Idempotent-Replayed": { + "description": "`true` when this response reports the recorded outcome of an earlier request with the same Idempotency-Key.", + "schema": { + "type": "string", + "enum": [ + "true" + ] + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "conflict_error", + "code": "idempotency_conflict", + "message": "This Idempotency-Key was already used with a different request. Use a new key for a different request.", + "param": null + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#idempotency-conflict" + } + } + } + }, "500": { "description": "Internal server error", "content": { @@ -45399,17 +45304,17 @@ } }, "x-mint": { - "content": "templates:manage" + "content": "campaigns:manage" } } }, - "/messaging/projects/{projectSlug}/campaigns": { + "/messaging/projects/{projectSlug}/campaigns/{id}": { "get": { - "operationId": "listCampaigns", + "operationId": "getCampaign", "tags": [ "Campaigns" ], - "summary": "List campaigns", + "summary": "Get a campaign", "security": [ { "BearerAuth": [] @@ -45424,6 +45329,14 @@ "required": true, "name": "projectSlug", "in": "path" + }, + { + "schema": { + "type": "string" + }, + "required": true, + "name": "id", + "in": "path" } ], "responses": { @@ -45432,7 +45345,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ListCampaignsResponse" + "$ref": "#/components/schemas/GetCampaignResponse" } } } @@ -45548,12 +45461,13 @@ }, "description": "**Required scope:** `campaigns:read`" }, - "post": { - "operationId": "createCampaign", + "patch": { + "operationId": "updateProjectCampaign", "tags": [ "Campaigns" ], - "summary": "Create a campaign", + "summary": "Update a campaign", + "description": "**Required scope:** `campaigns:manage`\n\nUpdates the fields you send and leaves the rest unchanged. On an unlaunched draft, a new `senderConfig` passes the same checks as create, on any plan; launch checks the selection against live plans. Once the campaign has launched, including a scheduled launch that has not started, a new `senderConfig` must pass the launch checks and can answer `402 campaigns_not_entitled` or `400 invalid_parameter`. `recipientListId` and a different `scheduledAt` are accepted only while the campaign is an unlaunched draft; afterwards they answer `409`. A sender change to a draft that launches while the change is being saved also answers `409`. A non-null `scheduledAt` must be integer Unix milliseconds within the JavaScript Date range.", "security": [ { "BearerAuth": [] @@ -45570,22 +45484,21 @@ "in": "path" }, { - "name": "Idempotency-Key", - "in": "header", - "required": false, - "description": "Unique key, 1 to 255 bytes of UTF-8, that makes this write safe to retry for 24 hours. A retry with the same key, credential, `Polymorfa-Version`, and request never runs the write again: it returns `409 idempotency_completed` when the original succeeded, or the original server error, with `Idempotent-Replayed: true`. Reusing the key for a different request returns `409 idempotency_conflict`; a retry while the original is still running returns `409 idempotency_in_progress`, and a retry after an unrecorded outcome returns `409 idempotency_outcome_unknown`.", "schema": { "type": "string", - "minLength": 1, - "maxLength": 255 - } + "format": "uuid" + }, + "required": true, + "name": "id", + "in": "path" } ], "requestBody": { + "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateCampaignRequest" + "$ref": "#/components/schemas/UpdateCampaignRequest" } } } @@ -45596,7 +45509,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateCampaignResponse" + "$ref": "#/components/schemas/UpdateCampaignResponse" } } } @@ -45644,7 +45557,7 @@ } }, "402": { - "description": "This project has no number on a plan that includes Campaigns (`campaigns_not_entitled`)", + "description": "`campaigns_not_entitled`: a new sender selection on a launched campaign names a number whose plan does not include Campaigns", "content": { "application/json": { "schema": { @@ -45653,13 +45566,13 @@ "example": { "error": { "type": "permission_error", - "code": "entitlement_required", - "message": "The account needs the required entitlement for this action.", + "code": "campaigns_not_entitled", + "message": "This number’s plan does not include Campaigns.", "param": null, "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" }, "data": null, - "docs": "https://docs.polymorfa.com/api/errors#entitlement-required" + "docs": "https://docs.polymorfa.com/api/errors#campaigns-not-entitled" } } } @@ -45707,25 +45620,7 @@ } }, "409": { - "description": "The Idempotency-Key already succeeded (`idempotency_completed`), was used for a different request (`idempotency_conflict`), belongs to a request that is still running (`idempotency_in_progress`, with Retry-After), or belongs to a request whose outcome was not recorded (`idempotency_outcome_unknown`)", - "headers": { - "Retry-After": { - "description": "Seconds to wait before retrying `idempotency_in_progress`.", - "schema": { - "type": "integer", - "minimum": 0 - } - }, - "Idempotent-Replayed": { - "description": "`true` when this response reports the recorded outcome of an earlier request with the same Idempotency-Key.", - "schema": { - "type": "string", - "enum": [ - "true" - ] - } - } - }, + "description": "`state_conflict`: the campaign has launched and the audience or start time can no longer change, or it launched while its sending numbers were being changed", "content": { "application/json": { "schema": { @@ -45734,12 +45629,13 @@ "example": { "error": { "type": "conflict_error", - "code": "idempotency_conflict", - "message": "This Idempotency-Key was already used with a different request. Use a new key for a different request.", - "param": null + "code": "state_conflict", + "message": "The request conflicts with the resource state.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" }, "data": null, - "docs": "https://docs.polymorfa.com/api/errors#idempotency-conflict" + "docs": "https://docs.polymorfa.com/api/errors#state-conflict" } } } @@ -45764,21 +45660,41 @@ } } } + }, + "503": { + "description": "`service_unavailable`: number eligibility for a launched campaign's new sender selection could not be checked. Nothing was changed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "service_unavailable", + "message": "A required service is unavailable. Check completion before repeating a write.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" + } + } + } } }, "x-mint": { "content": "campaigns:manage" - }, - "description": "**Required scope:** `campaigns:manage`" + } } }, - "/messaging/projects/{projectSlug}/campaigns/{id}": { + "/messaging/projects/{projectSlug}/campaigns/{id}/analytics": { "get": { - "operationId": "getCampaign", + "operationId": "getCampaignAnalytics", "tags": [ "Campaigns" ], - "summary": "Get a campaign", + "summary": "Campaign analytics (funnel, response timing, retries)", "security": [ { "BearerAuth": [] @@ -45809,7 +45725,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetCampaignResponse" + "$ref": "#/components/schemas/CampaignAnalyticsResponse" } } } @@ -45856,27 +45772,6 @@ } } }, - "402": { - "description": "This project has no number on a plan that includes Campaigns (`campaigns_not_entitled`)", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "permission_error", - "code": "entitlement_required", - "message": "The account needs the required entitlement for this action.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#entitlement-required" - } - } - } - }, "403": { "description": "Authenticated but not permitted (missing scope or rule violation)", "content": { @@ -45947,19 +45842,20 @@ "description": "**Required scope:** `campaigns:read`" } }, - "/messaging/projects/{projectSlug}/campaigns/{id}/analytics": { - "get": { - "operationId": "getCampaignAnalytics", + "/messaging/projects/{projectSlug}/campaigns/{id}/launch": { + "post": { + "operationId": "launchCampaign", "tags": [ "Campaigns" ], - "summary": "Campaign analytics (funnel, response timing, retries)", + "summary": "Launch a campaign", + "description": "**Required scope:** `campaigns:manage`\n\nStarts a draft campaign. It begins sending at `scheduledAt`, or right away when no start time is set. Launch checks the saved sender selection against live plans: `402 campaigns_not_entitled` when a selected number is missing or its plan does not include Campaigns, or when an unselected campaign's project has no such number; `400 invalid_parameter` on `senderConfig.sessionIds` when a selected number is not connected or the selection mixes sending types; `400 campaign_throughput_capped` when a requested throughput is above its ceiling. The response returns the campaign as it was when the launch was accepted.", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "campaigns:read", + "x-required-scope": "campaigns:manage", "parameters": [ { "schema": { @@ -45976,15 +45872,35 @@ "required": true, "name": "id", "in": "path" + }, + { + "name": "Idempotency-Key", + "in": "header", + "required": false, + "description": "Unique key, 1 to 255 bytes of UTF-8, that makes this write safe to retry for 24 hours. A retry with the same key, credential, `Polymorfa-Version`, and request never runs the write again: it returns `409 idempotency_completed` when the original succeeded, or the original server error, with `Idempotent-Replayed: true`. Reusing the key for a different request returns `409 idempotency_conflict`; a retry while the original is still running returns `409 idempotency_in_progress`, and a retry after an unrecorded outcome returns `409 idempotency_outcome_unknown`.", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 255 + } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LaunchCampaignRequest" + } + } + } + }, "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CampaignAnalyticsResponse" + "$ref": "#/components/schemas/LaunchCampaignResponse" } } } @@ -46032,7 +45948,7 @@ } }, "402": { - "description": "This project has no number on a plan that includes Campaigns (`campaigns_not_entitled`)", + "description": "`campaigns_not_entitled`: a selected number is missing or its plan does not include Campaigns, or the project has no number on such a plan. Drafts are saved on every plan.", "content": { "application/json": { "schema": { @@ -46041,13 +45957,13 @@ "example": { "error": { "type": "permission_error", - "code": "entitlement_required", - "message": "The account needs the required entitlement for this action.", + "code": "campaigns_not_entitled", + "message": "This number’s plan does not include Campaigns.", "param": null, "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" }, "data": null, - "docs": "https://docs.polymorfa.com/api/errors#entitlement-required" + "docs": "https://docs.polymorfa.com/api/errors#campaigns-not-entitled" } } } @@ -46094,6 +46010,45 @@ } } }, + "409": { + "description": "`state_conflict`: The campaign is not a draft, it is already launched and waiting for its start time, or its sending numbers changed during the launch. The Idempotency-Key already succeeded (`idempotency_completed`), was used for a different request (`idempotency_conflict`), belongs to a request that is still running (`idempotency_in_progress`, with Retry-After), or belongs to a request whose outcome was not recorded (`idempotency_outcome_unknown`)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "conflict_error", + "code": "state_conflict", + "message": "The request conflicts with the resource state.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#state-conflict" + } + } + }, + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying `idempotency_in_progress`.", + "schema": { + "type": "integer", + "minimum": 0 + } + }, + "Idempotent-Replayed": { + "description": "`true` when this response reports the recorded outcome of an earlier request with the same Idempotency-Key.", + "schema": { + "type": "string", + "enum": [ + "true" + ] + } + } + } + }, "500": { "description": "Internal server error", "content": { @@ -46114,22 +46069,42 @@ } } } + }, + "503": { + "description": "`service_unavailable`: the selected numbers' plans could not be checked; retry", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "service_unavailable", + "message": "A required service is unavailable. Check completion before repeating a write.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" + } + } + } } }, "x-mint": { - "content": "campaigns:read" - }, - "description": "**Required scope:** `campaigns:read`" + "content": "campaigns:manage" + } } }, - "/messaging/projects/{projectSlug}/campaigns/{id}/launch": { + "/messaging/projects/{projectSlug}/campaigns/{id}/pause": { "post": { - "operationId": "launchCampaign", + "operationId": "pauseCampaign", "tags": [ "Campaigns" ], - "summary": "Launch a campaign", - "description": "**Required scope:** `campaigns:manage`\n\nStarts a draft campaign. It begins sending at `scheduledAt`, or right away when no start time is set. The response returns the campaign as it was when the launch was accepted.", + "summary": "Pause a campaign", + "description": "**Required scope:** `campaigns:manage`\n\nPauses a running campaign. Sends already in progress can still finish.", "security": [ { "BearerAuth": [] @@ -46165,22 +46140,13 @@ } } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/LaunchCampaignRequest" - } - } - } - }, "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/LaunchCampaignResponse" + "$ref": "#/components/schemas/PauseCampaignResponse" } } } @@ -46227,27 +46193,6 @@ } } }, - "402": { - "description": "This project has no number on a plan that includes Campaigns (`campaigns_not_entitled`)", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "permission_error", - "code": "entitlement_required", - "message": "The account needs the required entitlement for this action.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#entitlement-required" - } - } - } - }, "403": { "description": "Authenticated but not permitted (missing scope or rule violation)", "content": { @@ -46291,7 +46236,7 @@ } }, "409": { - "description": "`state_conflict`: The campaign is not a draft, or it is already launched and waiting for its start time. The Idempotency-Key already succeeded (`idempotency_completed`), was used for a different request (`idempotency_conflict`), belongs to a request that is still running (`idempotency_in_progress`, with Retry-After), or belongs to a request whose outcome was not recorded (`idempotency_outcome_unknown`)", + "description": "`state_conflict`: The campaign is not running, or it has no active delivery run to pause. The Idempotency-Key already succeeded (`idempotency_completed`), was used for a different request (`idempotency_conflict`), belongs to a request that is still running (`idempotency_in_progress`, with Retry-After), or belongs to a request whose outcome was not recorded (`idempotency_outcome_unknown`)", "content": { "application/json": { "schema": { @@ -46356,14 +46301,14 @@ } } }, - "/messaging/projects/{projectSlug}/campaigns/{id}/pause": { + "/messaging/projects/{projectSlug}/campaigns/{id}/resume": { "post": { - "operationId": "pauseCampaign", + "operationId": "resumeCampaign", "tags": [ "Campaigns" ], - "summary": "Pause a campaign", - "description": "**Required scope:** `campaigns:manage`\n\nPauses a running campaign. Sends already in progress can still finish.", + "summary": "Resume a campaign", + "description": "**Required scope:** `campaigns:manage`\n\nResumes a paused campaign. Its remaining recipients are sent after the same checks as before the pause.", "security": [ { "BearerAuth": [] @@ -46405,7 +46350,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PauseCampaignResponse" + "$ref": "#/components/schemas/ResumeCampaignResponse" } } } @@ -46452,27 +46397,6 @@ } } }, - "402": { - "description": "This project has no number on a plan that includes Campaigns (`campaigns_not_entitled`)", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "permission_error", - "code": "entitlement_required", - "message": "The account needs the required entitlement for this action.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#entitlement-required" - } - } - } - }, "403": { "description": "Authenticated but not permitted (missing scope or rule violation)", "content": { @@ -46516,7 +46440,7 @@ } }, "409": { - "description": "`state_conflict`: The campaign is not running, or it has no active delivery run to pause. The Idempotency-Key already succeeded (`idempotency_completed`), was used for a different request (`idempotency_conflict`), belongs to a request that is still running (`idempotency_in_progress`, with Retry-After), or belongs to a request whose outcome was not recorded (`idempotency_outcome_unknown`)", + "description": "`state_conflict`: The campaign is not paused, or it has no active delivery run to resume. The Idempotency-Key already succeeded (`idempotency_completed`), was used for a different request (`idempotency_conflict`), belongs to a request that is still running (`idempotency_in_progress`, with Retry-After), or belongs to a request whose outcome was not recorded (`idempotency_outcome_unknown`)", "content": { "application/json": { "schema": { @@ -46581,14 +46505,14 @@ } } }, - "/messaging/projects/{projectSlug}/campaigns/{id}/resume": { + "/messaging/projects/{projectSlug}/campaigns/{id}/stop": { "post": { - "operationId": "resumeCampaign", + "operationId": "stopCampaign", "tags": [ "Campaigns" ], - "summary": "Resume a campaign", - "description": "**Required scope:** `campaigns:manage`\n\nResumes a paused campaign. Its remaining recipients are sent after the same checks as before the pause.", + "summary": "Stop a campaign", + "description": "**Required scope:** `campaigns:manage`\n\nCancels a draft, running, or paused campaign. Recipients not yet sent are not sent. The campaign moves to `cancelling` and then to `cancelled` once sends already in progress finish; a campaign with no active delivery run is `cancelled` immediately.", "security": [ { "BearerAuth": [] @@ -46630,7 +46554,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ResumeCampaignResponse" + "$ref": "#/components/schemas/StopCampaignResponse" } } } @@ -46677,27 +46601,6 @@ } } }, - "402": { - "description": "This project has no number on a plan that includes Campaigns (`campaigns_not_entitled`)", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "permission_error", - "code": "entitlement_required", - "message": "The account needs the required entitlement for this action.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#entitlement-required" - } - } - } - }, "403": { "description": "Authenticated but not permitted (missing scope or rule violation)", "content": { @@ -46741,7 +46644,7 @@ } }, "409": { - "description": "`state_conflict`: The campaign is not paused, or it has no active delivery run to resume. The Idempotency-Key already succeeded (`idempotency_completed`), was used for a different request (`idempotency_conflict`), belongs to a request that is still running (`idempotency_in_progress`, with Retry-After), or belongs to a request whose outcome was not recorded (`idempotency_outcome_unknown`)", + "description": "`state_conflict`: The campaign is already finished or cancelled. The Idempotency-Key already succeeded (`idempotency_completed`), was used for a different request (`idempotency_conflict`), belongs to a request that is still running (`idempotency_in_progress`, with Retry-After), or belongs to a request whose outcome was not recorded (`idempotency_outcome_unknown`)", "content": { "application/json": { "schema": { @@ -46806,14 +46709,14 @@ } } }, - "/messaging/projects/{projectSlug}/campaigns/{id}/stop": { + "/messaging/projects/{projectSlug}/campaigns/{id}/reschedule": { "post": { - "operationId": "stopCampaign", + "operationId": "rescheduleCampaign", "tags": [ "Campaigns" ], - "summary": "Stop a campaign", - "description": "**Required scope:** `campaigns:manage`\n\nCancels a draft, running, or paused campaign. Recipients not yet sent are not sent. The campaign moves to `cancelling` and then to `cancelled` once sends already in progress finish; a campaign with no active delivery run is `cancelled` immediately.", + "summary": "Reschedule a campaign", + "description": "**Required scope:** `campaigns:manage`\n\nMoves the start of a launched campaign that is still waiting for it. Set `scheduledAt` to a later or earlier time, or to `null` to start now. The response returns the campaign with its new `scheduledAt`. Sending the current start time again changes nothing. A campaign that has started, finished, is being stopped, or was never launched is refused with `409`; launch a draft with `scheduledAt` instead.", "security": [ { "BearerAuth": [] @@ -46849,13 +46752,23 @@ } } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RescheduleCampaignRequest" + } + } + } + }, "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/StopCampaignResponse" + "$ref": "#/components/schemas/RescheduleCampaignResponse" } } } @@ -46902,27 +46815,6 @@ } } }, - "402": { - "description": "This project has no number on a plan that includes Campaigns (`campaigns_not_entitled`)", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "permission_error", - "code": "entitlement_required", - "message": "The account needs the required entitlement for this action.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#entitlement-required" - } - } - } - }, "403": { "description": "Authenticated but not permitted (missing scope or rule violation)", "content": { @@ -46966,7 +46858,7 @@ } }, "409": { - "description": "`state_conflict`: The campaign is already finished or cancelled. The Idempotency-Key already succeeded (`idempotency_completed`), was used for a different request (`idempotency_conflict`), belongs to a request that is still running (`idempotency_in_progress`, with Retry-After), or belongs to a request whose outcome was not recorded (`idempotency_outcome_unknown`)", + "description": "`state_conflict`: The campaign has already started, is finished or being stopped, or was never launched. The Idempotency-Key already succeeded (`idempotency_completed`), was used for a different request (`idempotency_conflict`), belongs to a request that is still running (`idempotency_in_progress`, with Retry-After), or belongs to a request whose outcome was not recorded (`idempotency_outcome_unknown`)", "content": { "application/json": { "schema": { @@ -47124,27 +47016,6 @@ } } }, - "402": { - "description": "This project has no number on a plan that includes Campaigns (`campaigns_not_entitled`)", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "permission_error", - "code": "entitlement_required", - "message": "The account needs the required entitlement for this action.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#entitlement-required" - } - } - } - }, "403": { "description": "Authenticated but not permitted (missing scope or rule violation)", "content": { @@ -52121,78 +51992,6 @@ } } }, - "bansafe.risk_changed": { - "post": { - "operationId": "onBansafeRiskChanged", - "summary": "A number's forecast risk level changed", - "tags": [ - "Webhooks" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BanSafeRiskChangedEvent" - } - } - } - }, - "responses": { - "2XX": { - "description": "Return any 2xx status to acknowledge receipt. Non-2xx responses are retried according to the retry policy." - } - } - } - }, - "bansafe.health_changed": { - "post": { - "operationId": "onBansafeHealthChanged", - "summary": "A number's measured health band changed", - "tags": [ - "Webhooks" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BanSafeHealthChangedEvent" - } - } - } - }, - "responses": { - "2XX": { - "description": "Return any 2xx status to acknowledge receipt. Non-2xx responses are retried according to the retry policy." - } - } - } - }, - "bansafe.enforcement": { - "post": { - "operationId": "onBansafeEnforcement", - "summary": "WhatsApp applied an enforcement to a number, or a ban was reported", - "tags": [ - "Webhooks" - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BanSafeEnforcementEvent" - } - } - } - }, - "responses": { - "2XX": { - "description": "Return any 2xx status to acknowledge receipt. Non-2xx responses are retried according to the retry policy." - } - } - } - }, "bansafe.action": { "post": { "operationId": "onBansafeAction", @@ -52433,6 +52232,30 @@ } } }, + "campaign.rescheduled": { + "post": { + "operationId": "onCampaignRescheduled", + "summary": "A scheduled campaign's start time moved", + "tags": [ + "Webhooks" + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CampaignRescheduledEvent" + } + } + } + }, + "responses": { + "2XX": { + "description": "Return any 2xx status to acknowledge receipt. Non-2xx responses are retried according to the retry policy." + } + } + } + }, "campaign.completed": { "post": { "operationId": "onCampaignCompleted", diff --git a/contracts/openapi.platform.json b/contracts/openapi.platform.json index b1db4d92..399f8488 100644 --- a/contracts/openapi.platform.json +++ b/contracts/openapi.platform.json @@ -3,7 +3,7 @@ "info": { "title": "Polymorfa Console & Management API", "version": "1.0.0", - "description": "Complete console/management surface. Resource-oriented REST: `{param}` path variables, single-word action sub-resources.\n\nNamespaces decide inclusion:\n- `/platform/…` — **public registry** (218 endpoints): each endpoint declares either a scoped `pmfa_` organization key or a verified live dashboard JWT, or dashboard-only authorization when a specific user's authority is required. Only implemented handlers are published to customers and SDKs.\n- `/console/…` — **console-only** (99 endpoints): verified dashboard identity. EXCLUDED from the published spec; reason per endpoint.\n\nThe published customer spec = this document filtered to `x-audience: public` endpoints with an implemented runtime handler.\nMost transitional body/response schemas remain loose (`additionalProperties: true`); session-control operations use explicit strict schemas." + "description": "Complete console/management surface. Resource-oriented REST: `{param}` path variables, single-word action sub-resources.\n\nNamespaces decide inclusion:\n- `/platform/…` — **public registry** (219 endpoints): each endpoint declares either a scoped `pmfa_` organization key or a verified live dashboard JWT, or dashboard-only authorization when a specific user's authority is required. Only implemented handlers are published to customers and SDKs.\n- `/console/…` — **console-only** (99 endpoints): verified dashboard identity. EXCLUDED from the published spec; reason per endpoint.\n\nThe published customer spec = this document filtered to `x-audience: public` endpoints with an implemented runtime handler.\nMost transitional body/response schemas remain loose (`additionalProperties: true`); session-control operations use explicit strict schemas." }, "servers": [ { @@ -15814,7 +15814,7 @@ "campaigns" ], "summary": "Create campaign", - "description": "Creates a draft campaign. It is refused with `403 bansafe_org_suspended` while the team is suspended, `402 campaigns_not_entitled` when no number the campaign can send from is on a plan that includes Campaigns, and `400 campaign_throughput_capped` when a requested throughput is above its safety ceiling.\n\nRequires `campaigns:manage`. Project tokens only access resources in their own project.", + "description": "Creates a draft campaign on any plan. `senderConfig.sessionIds` may name numbers that are not yet on a plan that includes Campaigns; launch checks the selection. It is refused with `403 bansafe_org_suspended` while the team is suspended, `400 invalid_parameter` when `senderConfig.sessionIds` is malformed, and `400 campaign_throughput_capped` when a per-number cap is above its safety ceiling.\n\nRequires `campaigns:manage`. Project tokens only access resources in their own project.", "x-audience": "public", "x-polymorfa-resource-boundary": "project", "x-excluded-from-public": false, @@ -15878,9 +15878,6 @@ "401": { "$ref": "#/components/responses/Unauthorized" }, - "402": { - "$ref": "#/components/responses/PaymentRequired" - }, "403": { "$ref": "#/components/responses/Forbidden" }, @@ -15971,7 +15968,7 @@ "campaigns" ], "summary": "Update campaign", - "description": "Updates a campaign. A new `senderConfig` passes the same Campaigns plan and throughput checks as create. `recipientListId` points the campaign at another audience while it is still an unlaunched draft and replaces its existing recipients; after launch it answers `409`.\n\nRequires `campaigns:manage`. Project tokens only access resources in their own project.", + "description": "Updates a campaign. On an unlaunched draft, a new `senderConfig` passes the same checks as create and plans are checked at launch. Once the campaign has launched, including a scheduled launch that has not started, a new `senderConfig` passes the launch checks and can answer `402 campaigns_not_entitled` or `400 invalid_parameter`. `recipientListId` points the campaign at another audience while it is still an unlaunched draft and replaces its existing recipients; after launch it answers `409`. A sender edit to a draft that launches while the edit is being saved also answers `409`. A changed `scheduledAt` after launch answers `409`; use reschedule to move a scheduled start. A non-null `scheduledAt` must be integer Unix milliseconds from zero through the JavaScript Date maximum.\n\nRequires `campaigns:manage`. Project tokens only access resources in their own project.", "x-audience": "public", "x-polymorfa-resource-boundary": "project", "x-excluded-from-public": false, @@ -16066,6 +16063,9 @@ }, "409": { "$ref": "#/components/responses/Conflict" + }, + "503": { + "$ref": "#/components/responses/CampaignEligibilityUnavailable" } } }, @@ -16175,7 +16175,7 @@ "campaigns" ], "summary": "Launch campaign", - "description": "Starts a draft campaign. It begins sending at `scheduledAt`, or right away when no start time is set. The launch passes the same suspension, Campaigns plan and throughput checks as create, against the campaign's stored settings. `409` means the campaign is not a draft or is already launched and waiting for its start time.\n\nRequires `campaigns:manage`. Project tokens only access resources in their own project.", + "description": "Starts a draft campaign. It begins sending at `scheduledAt`, or right away when no start time is set. Launch checks the stored sender selection against live plans: `402 campaigns_not_entitled` when a selected number is missing or its plan does not include Campaigns, or when an unselected campaign's project has no such number; `400 invalid_parameter` on `senderConfig.sessionIds` when a selected number is not connected or the selection mixes sending types; `400 campaign_throughput_capped` when a requested throughput is above its ceiling. `409` means the campaign is not a draft, is already launched and waiting for its start time, or its sending numbers changed during the launch.\n\nRequires `campaigns:manage`. Project tokens only access resources in their own project.", "x-audience": "public", "x-polymorfa-resource-boundary": "project", "x-excluded-from-public": false, @@ -16301,6 +16301,9 @@ }, "409": { "$ref": "#/components/responses/Conflict" + }, + "503": { + "$ref": "#/components/responses/CampaignEligibilityUnavailable" } } } @@ -16699,6 +16702,139 @@ } } }, + "/platform/campaigns/{campaignId}/reschedule": { + "post": { + "operationId": "reschedulePlatformCampaign", + "tags": [ + "campaigns" + ], + "summary": "Reschedule campaign", + "description": "Moves the start of a launched campaign that is still waiting for it. Set `scheduledAt` to a later or earlier time, or to `null` to start now. The response returns the campaign with its new `scheduledAt`; sending the current start time again changes nothing. A suspended team is refused with `403 bansafe_org_suspended`. `409` means the campaign has already started, is finished or being stopped, or was never launched; launch a draft with `scheduledAt` instead.\n\nRequires `campaigns:manage`. Project tokens only access resources in their own project.", + "x-audience": "public", + "x-polymorfa-resource-boundary": "project", + "x-excluded-from-public": false, + "x-required-scope": "campaigns:manage", + "security": [ + { + "ApiKeyAuth": [] + }, + { + "ConsoleSession": [] + } + ], + "parameters": [ + { + "name": "campaignId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "Idempotency-Key", + "in": "header", + "required": false, + "description": "Optional unique key, 1 to 255 bytes, that makes this request safe to retry for 24 hours. A retry with the same key, credential, `Polymorfa-Version` and request never runs it again: it returns `409 idempotency_completed` when the original succeeded, or the original server error. Reusing the key for a different request returns `409 idempotency_conflict`; a retry while the original is still running returns `409 idempotency_in_progress`, and a retry after an unrecorded outcome returns `409 idempotency_outcome_unknown`.", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 255 + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ReschedulePlatformCampaignRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "headers": { + "X-Request-Id": { + "schema": { + "type": "string" + }, + "description": "Stable request correlation identifier." + }, + "Polymorfa-Version": { + "schema": { + "type": "string" + }, + "description": "API version applied to this response." + } + }, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": false, + "properties": { + "data": { + "type": "object", + "additionalProperties": true, + "required": [ + "id", + "status", + "operationId" + ], + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "status": { + "type": "string", + "enum": [ + "draft", + "running", + "paused", + "cancelling", + "cancelled", + "completed", + "failed", + "archived" + ] + }, + "operationId": { + "type": "string", + "format": "uuid", + "description": "The operation that carries this lifecycle action." + } + } + } + }, + "required": [ + "data" + ] + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "409": { + "$ref": "#/components/responses/Conflict" + } + } + } + }, "/platform/campaigns/{campaignId}/archive": { "post": { "operationId": "archiveCampaign", @@ -21312,7 +21448,7 @@ "tags": [ "webhooks" ], - "summary": "Send an team webhook test", + "summary": "Send a team webhook test", "description": "Creates a synthetic team event and a traceable webhook delivery.\n\nRequires a team API key with `webhooks:manage`. Project tokens are not accepted.", "x-audience": "public", "x-polymorfa-resource-boundary": "organization", @@ -21352,7 +21488,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TestWebhookInput" + "$ref": "#/components/schemas/TestOrganizationWebhookInput" } } } @@ -22100,7 +22236,7 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TestWebhookInput" + "$ref": "#/components/schemas/TestProjectWebhookInput" } } } @@ -31346,6 +31482,7 @@ "available", "enrolled", "variant", + "experimentRevision", "experimentAssigned" ], "properties": { @@ -31368,6 +31505,12 @@ "type": "string", "nullable": true }, + "experimentRevision": { + "type": "integer", + "minimum": 1, + "nullable": true, + "description": "Published experiment policy revision for an assigned native experiment; null otherwise." + }, "experimentAssigned": { "type": "boolean", "description": "True only when an experiment assigned a registered variant with measurement enabled." @@ -37478,6 +37621,27 @@ "name" ] }, + "ReschedulePlatformCampaignRequest": { + "type": "object", + "additionalProperties": false, + "properties": { + "scheduledAt": { + "type": "integer", + "minimum": 1, + "maximum": 8640000000000000, + "nullable": true, + "description": "The new start time in Unix milliseconds. `null`, or a time that is not in the future, starts the campaign now." + }, + "projectId": { + "type": "string", + "format": "uuid", + "description": "Required when using a team API key. Project tokens derive the project from the credential." + } + }, + "required": [ + "scheduledAt" + ] + }, "CreateAudienceRequest": { "type": "object", "additionalProperties": false, @@ -42575,7 +42739,19 @@ "additionalProperties": false, "maxProperties": 0 }, - "TestWebhookInput": { + "TestOrganizationWebhookInput": { + "type": "object", + "additionalProperties": false, + "description": "A team webhook test accepts an optional event type. Supplied event bodies and session IDs are rejected with HTTP 400.", + "properties": { + "eventType": { + "type": "string", + "default": "polymorfa.test", + "pattern": "^[a-z0-9][a-z0-9_.-]{0,127}$" + } + } + }, + "TestProjectWebhookInput": { "description": "Names reserved for internal use are rejected with HTTP 400, whether supplied as eventType or in the native event body, before a test event or delivery is created.", "oneOf": [ { @@ -47421,6 +47597,30 @@ } } }, + "CampaignEligibilityUnavailable": { + "description": "`service_unavailable`: the selected numbers' plans could not be checked. Nothing was launched or saved; retry the request.", + "headers": { + "X-Request-Id": { + "schema": { + "type": "string" + }, + "description": "Stable request correlation identifier." + }, + "Polymorfa-Version": { + "schema": { + "type": "string" + }, + "description": "API version applied to this response." + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorEnvelope" + } + } + } + }, "CallControlUnavailable": { "description": "The call service could not be reached. Nothing was changed; retry the request.", "headers": { diff --git a/contracts/source.json b/contracts/source.json index 065ea563..46af627d 100644 --- a/contracts/source.json +++ b/contracts/source.json @@ -1,19 +1,19 @@ { "repository": "polymorfa/polymorfa", - "commit": "5cba4237fcec1ef776e8d3727c7ecc4d8f0207a3", - "branch": "dev", - "published": true, - "note": "Exact merged API dev commit integrating Hybrid Link and campaign.launched, campaign.resumed, and campaign.stopped webhook schemas from feature source 3558c289ec35aba45a498a8794a94b4919365899. Both snapshots are byte-identical to the authoritative source files. SDK package publication is separate.", + "commit": "2f24c1d62", + "branch": "codex/native-release-capabilities-20260926", + "published": false, + "note": "Coordinated with polymorfa/polymorfa PR #388 at source commit 2f24c1d62. Both snapshots are byte-identical to the authoritative source files. SDK package publication is separate.", "contracts": { "messaging": { "sourcePath": "apps/api/docs/openapi.json", "snapshotPath": "contracts/openapi.messaging.json", - "sha256": "7291c6001b8d5f53c368b8775d46d137868d6c75b9ed63f673d8c255242d62ab" + "sha256": "959f28d1181a085da7927b1f10240b06130de6b8a0b4a3f80fbbea9c710e0262" }, "platform": { "sourcePath": "apps/api/docs/openapi.management.json", "snapshotPath": "contracts/openapi.platform.json", - "sha256": "4e5aa9b8890b8963d704df22efbbce7c6bd9ac09b9253f5c8215bb3966b2f2cc" + "sha256": "17a3d97d9b8ba6b4a1e0d7d1fbc24b6be1cf579165fa02f8b7358cc6c5029892" } } } diff --git a/contracts/testing-events.json b/contracts/testing-events.json index 058e09f0..8be5cddc 100644 --- a/contracts/testing-events.json +++ b/contracts/testing-events.json @@ -1,8 +1,8 @@ { "repository": "polymorfa/polymorfa", - "commit": "9c876c16c60b74370d934e1275f23ef6096bee12", + "commit": "2f24c1d62", "sourcePath": "apps/api/docs/openapi.json", - "sourceSha256": "1b2c37fc3f89360bc9cc92c3deeb96ca8c42349e36dff8cb7c9066bf1556ae4e", + "sourceSha256": "959f28d1181a085da7927b1f10240b06130de6b8a0b4a3f80fbbea9c710e0262", "schemas": { "TestEventFixtureList": { "type": "object", @@ -23,9 +23,7 @@ "call.ended", "session.status", "session.restriction_updated", - "template.status", - "bansafe.enforcement", - "bansafe.risk_changed" + "template.status" ] }, "description": { @@ -64,9 +62,7 @@ "call.ended", "session.status", "session.restriction_updated", - "template.status", - "bansafe.enforcement", - "bansafe.risk_changed" + "template.status" ], "description": "Fixture name. Each name is the event type it produces." }, @@ -97,9 +93,7 @@ "call.ended", "session.status", "session.restriction_updated", - "template.status", - "bansafe.enforcement", - "bansafe.risk_changed" + "template.status" ] }, "session": { @@ -215,14 +209,6 @@ "type": "string", "minLength": 1, "maxLength": 256 - }, - "enforcementKind": { - "type": "string", - "enum": ["cap_warning", "cap_reached", "timelock", "temporary_ban"] - }, - "riskLevel": { - "type": "string", - "enum": ["low", "elevated", "high", "critical"] } }, "additionalProperties": false diff --git a/packages/typescript/src/index.ts b/packages/typescript/src/index.ts index 49aaf94d..4c927b17 100644 --- a/packages/typescript/src/index.ts +++ b/packages/typescript/src/index.ts @@ -335,6 +335,8 @@ export type { CreateBusinessQuickReplyResponse, CreateCampaignRequest, CreateCampaignResponse, + UpdateCampaignRequest, + UpdateCampaignResponse, CreateChannelRequest, CreateChannelResponse, CreateGroupRequest, @@ -486,6 +488,8 @@ export type { ResolveIdentityResponse, ReactToChannelMessageResponse, RequeueCampaignRequest, + RescheduleCampaignRequest, + RescheduleCampaignResponse, RevokeGroupInviteCodeResponse, RequestPairCodeResponse, SeenRequest, @@ -797,6 +801,7 @@ export type { ListPlatformCampaignRecipientsParams, OptOutSettings, PlatformCampaignParams, + ReschedulePlatformCampaignRequest, PlatformCampaignRecipientsEnvelope, UpdateOptOutSettingsRequest, UpdatePlatformCampaignRequest, diff --git a/packages/typescript/src/messaging/campaigns.ts b/packages/typescript/src/messaging/campaigns.ts index 84bd20a3..cd0a6dc0 100644 --- a/packages/typescript/src/messaging/campaigns.ts +++ b/packages/typescript/src/messaging/campaigns.ts @@ -19,6 +19,10 @@ import type { ListCampaignRecipientsResponse, ListCampaignsResponse, RequeueCampaignRequest, + RescheduleCampaignRequest, + RescheduleCampaignResponse, + UpdateCampaignRequest, + UpdateCampaignResponse, } from "./types.js"; /** Exact project-slug campaign workflow exposed by the Messaging API. */ @@ -61,6 +65,20 @@ export class MessagingCampaignsResource { }); } + update( + projectSlug: string, + campaignId: string, + body: UpdateCampaignRequest, + options: RequestOptions = {}, + ): Promise> { + return this.transport.request({ + method: "PATCH", + path: campaignPath(projectSlug, campaignId), + body, + ...withoutAutomaticRetry(options), + }); + } + analytics( projectSlug: string, campaignId: string, @@ -87,6 +105,20 @@ export class MessagingCampaignsResource { }); } + reschedule( + projectSlug: string, + campaignId: string, + body: RescheduleCampaignRequest, + options: RequestOptions = {}, + ): Promise> { + return this.transport.request({ + method: "POST", + path: `${campaignPath(projectSlug, campaignId)}/reschedule`, + body, + ...withIdempotencyKey(options), + }); + } + pause( projectSlug: string, campaignId: string, diff --git a/packages/typescript/src/messaging/testing-configuration.ts b/packages/typescript/src/messaging/testing-configuration.ts index 91adcd4a..4708a66e 100644 --- a/packages/typescript/src/messaging/testing-configuration.ts +++ b/packages/typescript/src/messaging/testing-configuration.ts @@ -33,8 +33,6 @@ export const TEST_EVENT_FIXTURES = [ "session.status", "session.restriction_updated", "template.status", - "bansafe.enforcement", - "bansafe.risk_changed", ] as const; /** Named test-event fixtures. Each name is the event type it produces. */ @@ -94,11 +92,6 @@ export interface TestEventOverrides { templateStatus?: "APPROVED" | "REJECTED"; /** template.status: rejection reason (REJECTED only). */ reason?: string; - /** bansafe.enforcement: outcome kind. */ - enforcementKind?: - "cap_warning" | "cap_reached" | "timelock" | "temporary_ban"; - /** bansafe.risk_changed: new risk level. */ - riskLevel?: "low" | "elevated" | "high" | "critical"; } export interface TriggerTestEventRequest { diff --git a/packages/typescript/src/messaging/types.ts b/packages/typescript/src/messaging/types.ts index 24a9fc92..56ca19af 100644 --- a/packages/typescript/src/messaging/types.ts +++ b/packages/typescript/src/messaging/types.ts @@ -224,6 +224,20 @@ export interface LaunchCampaignRequest { readonly scheduledAt?: number; } +/** At least one draft field must be supplied; the API rejects an empty object. */ +export interface UpdateCampaignRequest { + readonly name?: string; + readonly recipientListId?: string | null; + readonly senderConfig?: Readonly>; + /** Epoch milliseconds, or null to start at launch. */ + readonly scheduledAt?: number | null; +} + +export interface RescheduleCampaignRequest { + /** Epoch milliseconds, or null to start a waiting campaign now. */ + readonly scheduledAt: number | null; +} + export interface RequeueCampaignRequest { readonly includeSkippedError?: boolean; } @@ -240,8 +254,10 @@ export interface CampaignRequeueResult { export type ListCampaignsResponse = SuccessEnvelope; export type GetCampaignResponse = SuccessEnvelope; export type CreateCampaignResponse = SuccessEnvelope; +export type UpdateCampaignResponse = SuccessEnvelope; export type CampaignAnalyticsResponse = SuccessEnvelope; export type CampaignOperationResponse = SuccessEnvelope; +export type RescheduleCampaignResponse = SuccessEnvelope; export type CampaignRequeueResponse = SuccessEnvelope; export type CampaignStopResponse = SuccessEnvelope; export type AddCampaignRecipientsResponse = diff --git a/packages/typescript/src/platform/campaigns.ts b/packages/typescript/src/platform/campaigns.ts index db1a56e8..a64c016f 100644 --- a/packages/typescript/src/platform/campaigns.ts +++ b/packages/typescript/src/platform/campaigns.ts @@ -14,6 +14,7 @@ import type { PlatformCampaignParams, PlatformCampaignRecipientsEnvelope, PlatformPayload, + ReschedulePlatformCampaignRequest, UpdatePlatformCampaignRequest, } from "./types.js"; @@ -108,6 +109,19 @@ export class CampaignsResource { return this.action(campaignId, "launch", body, options); } + reschedule( + campaignId: string, + body: ReschedulePlatformCampaignRequest, + options: RequestOptions = {}, + ): CampaignResponse { + return this.transport.request({ + method: "POST", + path: `${campaignPath(campaignId)}/reschedule`, + body, + ...withIdempotencyKey(options), + }); + } + pause( campaignId: string, body?: PlatformPayload, diff --git a/packages/typescript/src/platform/developer-resources.ts b/packages/typescript/src/platform/developer-resources.ts index 29fefdab..ef586d92 100644 --- a/packages/typescript/src/platform/developer-resources.ts +++ b/packages/typescript/src/platform/developer-resources.ts @@ -41,6 +41,7 @@ import type { OrganizationWebhookMutationReceipt, OrganizationWebhookSecretRotationReceipt, OrganizationWebhookTestReceipt, + OrganizationWebhookTestInput, ProjectEvent, ProjectEventReplayReceipt, ProjectWebhook, @@ -112,6 +113,9 @@ type WebhookRotationFor = O extends "project" type WebhookTestFor = O extends "project" ? ProjectWebhookTestReceipt : OrganizationWebhookTestReceipt; +type WebhookTestInputFor = O extends "project" + ? TestWebhookInput + : OrganizationWebhookTestInput; type DeliveryFor = O extends "project" ? ProjectWebhookDelivery : OrganizationWebhookDelivery; @@ -422,7 +426,7 @@ export class WebhooksResource extends ResourceBase { } test( webhookId: string, - input: TestWebhookInput = {}, + input: WebhookTestInputFor = {} as WebhookTestInputFor, options: RequestOptions = {}, ): Promise>> { return this.mutate( diff --git a/packages/typescript/src/platform/developer-types.ts b/packages/typescript/src/platform/developer-types.ts index 7154d7d1..6114a6ea 100644 --- a/packages/typescript/src/platform/developer-types.ts +++ b/packages/typescript/src/platform/developer-types.ts @@ -165,6 +165,11 @@ export interface RotateWebhookSecretInput { readonly overlapSeconds?: number; } export type RetryWebhookDeliveryInput = Readonly>; +export interface OrganizationWebhookTestInput { + readonly eventType?: string; + readonly body?: never; + readonly sessionId?: never; +} export type TestWebhookInput = | { readonly eventType?: string; diff --git a/packages/typescript/src/platform/types.ts b/packages/typescript/src/platform/types.ts index e1b55ff0..8f7546af 100644 --- a/packages/typescript/src/platform/types.ts +++ b/packages/typescript/src/platform/types.ts @@ -211,6 +211,11 @@ export interface PlatformCampaignParams { readonly projectId: string; } +export interface ReschedulePlatformCampaignRequest extends PlatformCampaignParams { + /** Epoch milliseconds, or null to start a waiting campaign now. */ + readonly scheduledAt: number | null; +} + /** * Body accepted by `campaigns.update`. * diff --git a/packages/typescript/test/campaigns.test.ts b/packages/typescript/test/campaigns.test.ts index 705517ca..c9429412 100644 --- a/packages/typescript/test/campaigns.test.ts +++ b/packages/typescript/test/campaigns.test.ts @@ -16,6 +16,8 @@ import { type GetCampaignResponse, type ListCampaignRecipientsResponse, type ListCampaignsResponse, + type RescheduleCampaignResponse, + type UpdateCampaignResponse, } from "../src/index.js"; import { startTestServer, @@ -117,6 +119,7 @@ async function campaignsServer(): Promise<{ request.path.endsWith("/launch") || request.path.endsWith("/pause") || request.path.endsWith("/resume") || + request.path.endsWith("/reschedule") || request.path.endsWith("/stop") ? { ...campaign, @@ -205,6 +208,45 @@ describe("MessagingClient campaigns", () => { ); }); + it("updates a draft and reschedules a waiting campaign with the exact request bodies", async () => { + const { client, requests } = await campaignsServer(); + const updated = await client.campaigns.update("launch/eu", campaign.id, { + name: "Autumn launch", + scheduledAt: null, + }); + const rescheduled = await client.campaigns.reschedule( + "launch/eu", + campaign.id, + { scheduledAt: null }, + { idempotencyKey: "campaign-reschedule-autumn" }, + ); + + expectTypeOf(updated).toEqualTypeOf>(); + expectTypeOf(rescheduled).toEqualTypeOf< + ApiResponse + >(); + expect( + requests.map(({ method, path, body }) => ({ method, path, body })), + ).toEqual([ + { + method: "PATCH", + path: `/messaging/projects/launch%2Feu/campaigns/${campaign.id}`, + body: '{"name":"Autumn launch","scheduledAt":null}', + }, + { + method: "POST", + path: `/messaging/projects/launch%2Feu/campaigns/${campaign.id}/reschedule`, + body: '{"scheduledAt":null}', + }, + ]); + expect(requests[1]?.headers["idempotency-key"]).toBe( + "campaign-reschedule-autumn", + ); + expect(rescheduled.data.data.operationId).toBe( + "018f0000-0000-7000-8000-000000000003", + ); + }); + it("submits durable lifecycle commands with operation IDs", async () => { const { client, requests } = await campaignsServer(); diff --git a/packages/typescript/test/coverage-reconciliation.test.ts b/packages/typescript/test/coverage-reconciliation.test.ts index 8685f421..59b16e25 100644 --- a/packages/typescript/test/coverage-reconciliation.test.ts +++ b/packages/typescript/test/coverage-reconciliation.test.ts @@ -196,7 +196,7 @@ describe("reconciled coverage evidence", () => { }; expect(source.repository).toBe("polymorfa/polymorfa"); // Repinning the reviewed source requires updating this regression gate too. - expect(source.commit).toBe("5cba4237fcec1ef776e8d3727c7ecc4d8f0207a3"); + expect(source.commit).toBe("2f24c1d62"); expect(ledger.sourceCommit).toBe(source.commit); expect(Object.keys(source.contracts).sort()).toEqual([ "messaging", diff --git a/packages/typescript/test/coverage.test.ts b/packages/typescript/test/coverage.test.ts index 2dd449e3..5fc5c315 100644 --- a/packages/typescript/test/coverage.test.ts +++ b/packages/typescript/test/coverage.test.ts @@ -173,9 +173,9 @@ describe("coverage checker", () => { const result = runRepositoryChecker(); expect(result.status, result.stderr).toBe(0); expect(result.report).toMatchObject({ - sourceCommit: "5cba4237fcec1ef776e8d3727c7ecc4d8f0207a3", - total: 509, - covered: 379, + sourceCommit: "2f24c1d62", + total: 512, + covered: 382, partial: 0, missing: 0, excluded: 130, diff --git a/packages/typescript/test/organization-webhook-test.test.ts b/packages/typescript/test/organization-webhook-test.test.ts new file mode 100644 index 00000000..e1a6ca93 --- /dev/null +++ b/packages/typescript/test/organization-webhook-test.test.ts @@ -0,0 +1,42 @@ +import { afterEach, expect, it } from "vitest"; +import { Client } from "../src/index.js"; +import { ORGANIZATION_API_KEY } from "./support/credentials.js"; +import { startTestServer, type TestServer } from "./support/http-server.js"; + +const servers: TestServer[] = []; +afterEach(async () => { + await Promise.all(servers.splice(0).map((server) => server.close())); +}); + +it("sends only the allowed organization webhook test body", async () => { + const server = await startTestServer(() => ({ + status: 200, + body: JSON.stringify({ data: { operationId: "operation-1" } }), + })); + servers.push(server); + const client = new Client({ + credential: { type: "organizationApiKey", value: ORGANIZATION_API_KEY }, + baseUrl: server.url, + }); + await client.webhooks.test("hook-1", { eventType: "polymorfa.test" }); + expect(server.requests[0]).toMatchObject({ + method: "POST", + path: "/platform/webhooks/hook-1/tests", + body: '{"eventType":"polymorfa.test"}', + }); +}); + +it("keeps project webhook test body authority separate", () => { + const client = new Client({ + credential: { type: "organizationApiKey", value: ORGANIZATION_API_KEY }, + }); + const invalidOrganizationBody = () => { + client.webhooks.test("hook-1", { + // @ts-expect-error organization webhook tests reject supplied bodies + body: { event: "x" }, + // @ts-expect-error organization webhook tests reject session IDs + sessionId: "session-1", + }); + }; + expect(invalidOrganizationBody).toBeTypeOf("function"); +}); diff --git a/packages/typescript/test/platform-campaign-create.test.ts b/packages/typescript/test/platform-campaign-create.test.ts index 2463ee39..58248550 100644 --- a/packages/typescript/test/platform-campaign-create.test.ts +++ b/packages/typescript/test/platform-campaign-create.test.ts @@ -4,6 +4,7 @@ import { Client, PolymorfaConflictError, type CreatePlatformCampaignRequest, + type ReschedulePlatformCampaignRequest, } from "../src/index.js"; import { ORGANIZATION_API_KEY } from "./support/credentials.js"; import { startTestServer, type TestServer } from "./support/http-server.js"; @@ -157,3 +158,33 @@ it("returns the archive receipt and surfaces an active-campaign conflict", async ["POST", "/platform/campaigns/campaign-2/archive"], ]); }); + +it("reschedules a waiting campaign with its project and safe retry key", async () => { + const server = await startTestServer(() => ({ + status: 200, + body: JSON.stringify({ + data: { id: "campaign-1", status: "running", operationId: "operation-1" }, + }), + })); + servers.push(server); + const client = new Client({ + credential: { type: "organizationApiKey", value: ORGANIZATION_API_KEY }, + baseUrl: server.url, + }); + const request: ReschedulePlatformCampaignRequest = { + projectId: body.projectId, + scheduledAt: null, + }; + const result = await client.campaigns.reschedule("campaign-1", request, { + idempotencyKey: "reschedule-campaign-1", + }); + expect(server.requests[0]).toMatchObject({ + method: "POST", + path: "/platform/campaigns/campaign-1/reschedule", + body: JSON.stringify(request), + }); + expect(server.requests[0]?.headers["idempotency-key"]).toBe( + "reschedule-campaign-1", + ); + expect(result.data.data).toMatchObject({ operationId: "operation-1" }); +}); diff --git a/packages/typescript/test/testing-events.test.ts b/packages/typescript/test/testing-events.test.ts index 5a36fdfa..5a2a9704 100644 --- a/packages/typescript/test/testing-events.test.ts +++ b/packages/typescript/test/testing-events.test.ts @@ -81,7 +81,7 @@ it("lists fixtures", async () => { expect(fetcher.mock.calls[0]![1]?.method).toBe("GET"); }); -it("matches the public fixture catalog at API 9c876c16c60b74370d934e1275f23ef6096bee12", () => { +it("matches the pinned public fixture catalog", () => { expect(TEST_EVENT_FIXTURES).toEqual( apiContract.schemas.TriggerTestEventRequest.properties.event.enum, ); From 8dc35625a46fbf474a82271444d3d6d3bd7b78e8 Mon Sep 17 00:00:00 2001 From: Rajeh Taher Date: Sun, 27 Sep 2026 01:00:53 +0200 Subject: [PATCH 2/9] fix(sdk): align webhook catalog with source revision --- contracts/README.md | 6 +- examples/full-platform/lib/webhooks.ts | 4 +- packages/typescript/README.md | 22 +-- packages/typescript/src/index.ts | 12 +- packages/typescript/src/webhooks/events.ts | 125 ++---------------- packages/typescript/src/webhooks/index.ts | 12 +- .../typescript/test/webhook-contract.test.ts | 117 ++-------------- .../test/webhook-event-types.test.ts | 13 -- 8 files changed, 41 insertions(+), 270 deletions(-) diff --git a/contracts/README.md b/contracts/README.md index dcb6bb63..b2686f91 100644 --- a/contracts/README.md +++ b/contracts/README.md @@ -161,9 +161,9 @@ The operation declares `409` for the refusal after launch, which the transport already maps to `PolymorfaConflictError`; no SDK change was needed for it. The webhook catalog adds `contact.opted_out` and `contact.opted_in` with the -exported `ContactOptPayload`. `bansafe.health_changed` and -`bansafe.risk_changed`, which the contract already defined, are now registered -too, with their payload types. +exported `ContactOptPayload`. That earlier revision registered +`bansafe.health_changed` and `bansafe.risk_changed`; the current API source has +retired both events and `bansafe.enforcement`. | Status | Operations | | ------------------- | ---------: | diff --git a/examples/full-platform/lib/webhooks.ts b/examples/full-platform/lib/webhooks.ts index d151dc98..fa9020e3 100644 --- a/examples/full-platform/lib/webhooks.ts +++ b/examples/full-platform/lib/webhooks.ts @@ -89,8 +89,8 @@ function handle(event: WebhookEvent): void { console.warn("logged out", event.session, event.payload.reason); } else if (isEvent(event, "campaign.completed")) { console.info("campaign completed", event.payload.campaignId); - } else if (isEvent(event, "bansafe.enforcement")) { - console.warn("BanSafe enforcement changed", event.session); + } else if (isEvent(event, "bansafe.action")) { + console.warn("BanSafe action changed", event.session, event.payload.action); } else if ( !isEvent(event, "call.received") && !isEvent(event, "call.ended") && diff --git a/packages/typescript/README.md b/packages/typescript/README.md index bebf5ba7..f1736747 100644 --- a/packages/typescript/README.md +++ b/packages/typescript/README.md @@ -1024,7 +1024,7 @@ These methods send an `Idempotency-Key` on every call: - `messages.send` and `messages.react` - `chats.editMessage` and `chats.deleteMessage` - `channels.reactToMessage` -- `campaigns.create` and `campaigns.launch` +- `campaigns.create`, `campaigns.launch`, and `campaigns.reschedule` If you don't pass `idempotencyKey`, the SDK generates a random UUID for the call. Every automatic retry of that call reuses the key, so the API never @@ -1719,6 +1719,16 @@ const launched = await messaging.campaigns.launch( console.log(launched.data.data.operationId, launched.metadata.requestId); ``` +Use `messaging.campaigns.update(projectSlug, campaignId, changes)` to change a +draft's name, audience, sender selection, or start time. The SDK sends this +write once by default because the route has no replay key. For a campaign that +has launched and is waiting to start, call +`messaging.campaigns.reschedule(projectSlug, campaignId, { scheduledAt })`. +Set `scheduledAt` to a Unix millisecond timestamp or `null` to start now. The +organization client has `client.campaigns.reschedule(campaignId, { projectId, +scheduledAt })` for the Platform route. Both reschedule methods accept an +optional idempotency key in request options. + Create accepts inline recipients, an audience ID in `recipientListId`, or both. Each append accepts up to 1,000 recipients before launch and reports duplicates and invalid rows. Appends have no declared replay contract: the SDK sends them @@ -1849,18 +1859,14 @@ if (isEvent(event, "history.sync")) { ``` The catalog also types Customer lifecycle events (`customer.*`), BanSafe events -(`bansafe.health_threshold`, `bansafe.health_changed`, `bansafe.risk_changed`, -`bansafe.enforcement`, `bansafe.action`, `bansafe.incident`, and -`bansafe.claim`), campaign progress and lifecycle events (`campaign.*`), +(`bansafe.health_threshold`, `bansafe.action`, `bansafe.incident`, and +`bansafe.claim`), campaign progress and lifecycle events (`campaign.*`, including +`campaign.rescheduled`), `call.permission_changed`, `message.failed`, and `template.status`. `message.failed` reports `blocked_by_safety` when BanSafe stops a send, with an optional `code` and `retryAfter` in seconds. Unknown event names still parse as `UnknownWebhookEvent`. -For `bansafe.health_changed`, `band` is a `BanSafeHealthBandName`: -`good`, `fair`, `poor`, `failing`, or `unknown`. `previousBand` uses the same -type, with `null` for the first evaluation. - `contact.sync` delivers a Meta Cloud API contact batch as `{ kind: "contacts", value }`. `message.echo` reports a message sent from the WhatsApp Business app on a connected Meta Cloud API number as diff --git a/packages/typescript/src/index.ts b/packages/typescript/src/index.ts index 4c927b17..89b3e752 100644 --- a/packages/typescript/src/index.ts +++ b/packages/typescript/src/index.ts @@ -1029,22 +1029,11 @@ export { type WebhookPayloadMap, type BanSafeActionPayload, type BanSafeClaimPayload, - type BanSafeEnforcementPayload, type BanSafeEventRung, - type BanSafeForecast, - type BanSafeHealthChangedPayload, - type BanSafeHealthBandName, - type BanSafeHealthFinding, - type BanSafeHealthPenalties, type BanSafeHealthThresholdPayload, type BanSafeIncidentEventKind, type BanSafeIncidentPayload, - type BanSafeModelRef, type BanSafeRequiredFinding, - type BanSafeRiskChangedPayload, - type BanSafeRiskFactor, - type BanSafeRiskFactorGroup, - type BanSafeRiskLevel, type CampaignCapReachedPayload, type CampaignColdBlockedPayload, type CampaignCompletedPayload, @@ -1054,6 +1043,7 @@ export { type CampaignRecipientFailedPayload, type CampaignRecipientSentPayload, type CampaignRecipientSkippedPayload, + type CampaignRescheduledPayload, type CampaignResumedPayload, type CampaignStoppedPayload, type CampaignThrottledPayload, diff --git a/packages/typescript/src/webhooks/events.ts b/packages/typescript/src/webhooks/events.ts index 274da610..d8d8313d 100644 --- a/packages/typescript/src/webhooks/events.ts +++ b/packages/typescript/src/webhooks/events.ts @@ -22,11 +22,8 @@ import type { export const KNOWN_WEBHOOK_EVENT_TYPES = [ "bansafe.action", "bansafe.claim", - "bansafe.enforcement", - "bansafe.health_changed", "bansafe.health_threshold", "bansafe.incident", - "bansafe.risk_changed", "blocklist.update", "business.quick_reply.update", "call.accepted", @@ -50,6 +47,7 @@ export const KNOWN_WEBHOOK_EVENT_TYPES = [ "campaign.recipient_failed", "campaign.recipient_sent", "campaign.recipient_skipped", + "campaign.rescheduled", "campaign.resumed", "campaign.stopped", "campaign.throttled", @@ -679,102 +677,6 @@ export type BanSafeIncidentEventKind = export type BanSafeEventRung = "none" | "notify" | "throttle" | "block_cold" | "suspend"; -export type BanSafeRiskLevel = "low" | "elevated" | "high" | "critical"; -export type BanSafeHealthBandName = - "good" | "fair" | "poor" | "failing" | "unknown"; - -export interface BanSafeForecast { - /** Probability (0-1) of a temporary or permanent ban within 7 days. */ - readonly days7: number; - readonly days14: number; - readonly days30: number; -} - -/** The factor group a risk factor belongs to. */ -export type BanSafeRiskFactorGroup = - | "volume" - | "cold_outreach" - | "restrictions" - | "engagement" - | "send_errors" - | "pattern" - | "traffic_mix" - | "number_age" - | "connection" - | "ban_history" - | "account" - | "workspace" - | "climate" - | "conversation" - | "solicitation" - | "reputation"; - -export interface BanSafeRiskFactor { - /** Feature key, or `group:`. */ - readonly key: string; - readonly group: BanSafeRiskFactorGroup; - readonly label: string; - readonly direction: "raises" | "lowers"; - readonly strength: "strong" | "moderate" | "slight"; - /** Share, as a whole percentage, of the raising or lowering total. */ - readonly impact: number; - readonly sentence: string; - readonly hint: string | null; -} - -export interface BanSafeModelRef { - readonly version: string; - readonly reliability: "prior" | "early" | "calibrated"; -} - -export interface BanSafeRiskChangedPayload { - /** The customer's own number in E.164 format. */ - readonly phoneNumber: string; - readonly level: BanSafeRiskLevel; - /** Null for the first evaluation of the number. */ - readonly previousLevel: BanSafeRiskLevel | null; - /** Risk score from 0 (lowest) to 100 (highest). */ - readonly score: number; - readonly forecast: BanSafeForecast; - /** Up to five contributing factors, ordered by impact. */ - readonly factors: readonly BanSafeRiskFactor[]; - readonly model: BanSafeModelRef; - readonly evaluatedAt: string; -} - -export interface BanSafeHealthPenalties { - readonly conduct: number; - readonly restriction: number; - readonly connection: number; -} - -export interface BanSafeHealthFinding { - readonly key: string; - readonly title: string; - readonly severity: "info" | "warning" | "critical"; - /** `not_measured` means the signal could not be measured for this number. */ - readonly status: "open" | "acknowledged" | "not_measured"; - /** Health points this finding costs. */ - readonly points: number; -} - -export interface BanSafeHealthChangedPayload { - readonly phoneNumber: string; - /** Health from 0 (worst) to 100 (best), or null when not measured. */ - readonly health: number | null; - readonly band: BanSafeHealthBandName; - readonly previousBand: BanSafeHealthBandName | null; - readonly state: - "measured" | "partial" | "measuring" | "restricted" | "banned"; - readonly penalties: BanSafeHealthPenalties; - readonly findings: readonly BanSafeHealthFinding[]; - readonly measuredChecks: number; - readonly totalChecks: number; - /** Messages allowed today under the warm-up plan; null or absent without one. */ - readonly allowance?: number | null; - readonly evaluatedAt: string; -} - export interface BanSafeHealthThresholdPayload { readonly sessionId: string; readonly projectId: string; @@ -790,18 +692,6 @@ export interface BanSafeHealthThresholdPayload { readonly actionId: string; } -export interface BanSafeEnforcementPayload { - readonly phoneNumber: string; - readonly kind: BanSafeIncidentEventKind; - readonly source: "runtime" | "customer"; - readonly code?: number; - readonly subCode?: number; - readonly reason?: string; - readonly enforcementType?: string; - readonly startedAt: string; - readonly endsAt?: string; -} - /** A finding that must be resolved before a BanSafe action can lift. */ export interface BanSafeRequiredFinding { readonly findingKey: string; @@ -939,6 +829,15 @@ export interface CampaignResumedPayload { readonly resumedAt: number; } +export interface CampaignRescheduledPayload { + readonly campaignId: string; + /** Previous start time in Unix milliseconds, or null. */ + readonly previousScheduledAt: number | null; + /** New start time in Unix milliseconds. */ + readonly scheduledAt: number; + readonly rescheduledAt: number; +} + export interface CampaignCompletedPayload { readonly campaignId: string; readonly sentCount: number; @@ -1071,11 +970,8 @@ export interface UsageRecordedPayload { export interface WebhookPayloadMap { readonly "bansafe.action": BanSafeActionPayload; readonly "bansafe.claim": BanSafeClaimPayload; - readonly "bansafe.enforcement": BanSafeEnforcementPayload; - readonly "bansafe.health_changed": BanSafeHealthChangedPayload; readonly "bansafe.health_threshold": BanSafeHealthThresholdPayload; readonly "bansafe.incident": BanSafeIncidentPayload; - readonly "bansafe.risk_changed": BanSafeRiskChangedPayload; readonly "blocklist.update": BlocklistUpdatePayload; readonly "business.quick_reply.update": BusinessQuickReplyUpdatePayload; readonly "call.accepted": CallAcceptedPayload; @@ -1099,6 +995,7 @@ export interface WebhookPayloadMap { readonly "campaign.recipient_failed": CampaignRecipientFailedPayload; readonly "campaign.recipient_sent": CampaignRecipientSentPayload; readonly "campaign.recipient_skipped": CampaignRecipientSkippedPayload; + readonly "campaign.rescheduled": CampaignRescheduledPayload; readonly "campaign.resumed": CampaignResumedPayload; readonly "campaign.stopped": CampaignStoppedPayload; readonly "campaign.throttled": CampaignThrottledPayload; diff --git a/packages/typescript/src/webhooks/index.ts b/packages/typescript/src/webhooks/index.ts index 598cdd44..47d4c8f3 100644 --- a/packages/typescript/src/webhooks/index.ts +++ b/packages/typescript/src/webhooks/index.ts @@ -3,22 +3,11 @@ export { isEvent, type BanSafeActionPayload, type BanSafeClaimPayload, - type BanSafeEnforcementPayload, type BanSafeEventRung, - type BanSafeForecast, - type BanSafeHealthChangedPayload, - type BanSafeHealthBandName, - type BanSafeHealthFinding, - type BanSafeHealthPenalties, type BanSafeHealthThresholdPayload, type BanSafeIncidentEventKind, type BanSafeIncidentPayload, - type BanSafeModelRef, type BanSafeRequiredFinding, - type BanSafeRiskChangedPayload, - type BanSafeRiskFactor, - type BanSafeRiskFactorGroup, - type BanSafeRiskLevel, type BlocklistChange, type BlocklistUpdatePayload, type BusinessQuickReplyUpdatePayload, @@ -46,6 +35,7 @@ export { type CampaignRecipientFailedPayload, type CampaignRecipientSentPayload, type CampaignRecipientSkippedPayload, + type CampaignRescheduledPayload, type CampaignResumedPayload, type CampaignStoppedPayload, type CampaignThrottledPayload, diff --git a/packages/typescript/test/webhook-contract.test.ts b/packages/typescript/test/webhook-contract.test.ts index b8ae30ba..cde4ee82 100644 --- a/packages/typescript/test/webhook-contract.test.ts +++ b/packages/typescript/test/webhook-contract.test.ts @@ -8,7 +8,6 @@ import { constructWebhookEvent, isEvent, type KnownWebhookEventType, - type BanSafeHealthBandName, type ProjectWebhookDeliveryAttempt, type OrganizationWebhookDeliveryAttempt, type SessionLoggedOutPayload, @@ -307,75 +306,6 @@ const PAYLOADS: { "actionId", ], ), - "bansafe.risk_changed": shape()( - { - phoneNumber: "+15551234567", - level: "elevated", - previousLevel: "low", - score: 37, - forecast: { days7: 0.02, days14: 0.06, days30: 0.11 }, - factors: [ - { - key: "cold_send_ratio", - group: "cold_outreach", - label: "Share of messages sent to people who never messaged you", - direction: "raises", - strength: "strong", - impact: 42, - sentence: - "38 of the 51 people you messaged had never messaged this number", - hint: "Warm up the number before sending to new contacts", - }, - ], - model: { version: "prior-v0", reliability: "prior" }, - evaluatedAt: AT, - }, - [ - "phoneNumber", - "level", - "previousLevel", - "score", - "forecast", - "factors", - "model", - "evaluatedAt", - ], - ), - "bansafe.health_changed": shape()( - { - phoneNumber: "+15551234567", - health: 62, - band: "fair", - previousBand: "good", - state: "measured", - penalties: { conduct: 12, restriction: 0, connection: 4 }, - findings: [ - { - key: "unsolicited_outreach", - title: "Messaging people who never wrote to you", - severity: "warning", - status: "open", - points: 8, - }, - ], - measuredChecks: 14, - totalChecks: 18, - allowance: 240, - evaluatedAt: AT, - }, - [ - "phoneNumber", - "health", - "band", - "previousBand", - "state", - "penalties", - "findings", - "measuredChecks", - "totalChecks", - "evaluatedAt", - ], - ), "call.permission_changed": shape()( { conversation: { @@ -398,20 +328,6 @@ const PAYLOADS: { "changedAt", ], ), - "bansafe.enforcement": shape()( - { - phoneNumber: "+15551234567", - kind: "temporary_ban", - source: "runtime", - code: 403, - subCode: 12, - reason: "Account restricted", - enforcementType: "reachout_timelock", - startedAt: AT, - endsAt: AT, - }, - ["phoneNumber", "kind", "source", "startedAt"], - ), "bansafe.action": shape()( { phoneNumber: "+15551234567", @@ -652,6 +568,15 @@ const PAYLOADS: { }, ["campaignId", "sentCount", "remainingCount", "resumedAt"], ), + "campaign.rescheduled": shape()( + { + ...campaign, + previousScheduledAt: null, + scheduledAt: 1_790_000_003_000, + rescheduledAt: 1_790_000_001_000, + }, + ["campaignId", "previousScheduledAt", "scheduledAt", "rescheduledAt"], + ), "campaign.completed": shape()( { ...campaign, @@ -866,9 +791,6 @@ describe("webhook catalog contract", () => { expectTypeOf().toEqualTypeOf< "none" | "notify" | "throttle" | "block_cold" | "suspend" | null >(); - expectTypeOf< - P["bansafe.health_changed"]["previousBand"] - >().toEqualTypeOf(); }); it("types logged-out reasons and the required integer code from the pinned contract", async () => { @@ -905,27 +827,6 @@ describe("webhook catalog contract", () => { expectTypeOf(event.payload).toEqualTypeOf(); expect(event.payload).toEqual({ reason: "device_removed", code: 401 }); }); - - it("limits the previous BanSafe health band to the published values", () => { - expectTypeOf().toEqualTypeOf< - "good" | "fair" | "poor" | "failing" | "unknown" | null - >(); - const schema = messaging.components.schemas.BanSafeHealthChangedPayload!; - expect(schema.properties?.previousBand?.enum).toEqual([ - "good", - "fair", - "poor", - "failing", - "unknown", - null, - ]); - expect( - violations(messaging, schema, { - ...PAYLOADS["bansafe.health_changed"].value, - previousBand: "other", - }), - ).toContain('$.previousBand="other" is outside the enum'); - }); }); describe("webhook delivery attempt contract", () => { diff --git a/packages/typescript/test/webhook-event-types.test.ts b/packages/typescript/test/webhook-event-types.test.ts index ccae9e9c..b614a30f 100644 --- a/packages/typescript/test/webhook-event-types.test.ts +++ b/packages/typescript/test/webhook-event-types.test.ts @@ -1,8 +1,6 @@ import { describe, expect, expectTypeOf, it } from "vitest"; import type { - BanSafeHealthBandName, - BanSafeHealthChangedPayload, BlocklistUpdatePayload, WhatsAppMessageIds, BusinessQuickReplyUpdatePayload, @@ -49,7 +47,6 @@ import type { WebhookPayloadMap, } from "../src/index.js"; import { KNOWN_WEBHOOK_EVENT_TYPES } from "../src/index.js"; -import type { BanSafeHealthBandName as WebhooksHealthBandName } from "../src/webhooks/index.js"; type ExpectedIdentityReference = { readonly id: string; @@ -430,16 +427,6 @@ type ExportedPayloads = { }; describe("webhook event payload types", () => { - it("exports the health band through the public webhook type surfaces", () => { - expectTypeOf< - BanSafeHealthChangedPayload["band"] - >().toEqualTypeOf(); - expectTypeOf().toEqualTypeOf(); - expectTypeOf().toEqualTypeOf< - "good" | "fair" | "poor" | "failing" | "unknown" - >(); - }); - it("maps every formerly opaque event family to its contract payload", () => { expectTypeOf().toEqualTypeOf(); expectTypeOf().toEqualTypeOf(); From b1091ea02bab654816a77ff74c7d8b0a8ac0c648 Mon Sep 17 00:00:00 2001 From: Rajeh Taher Date: Sun, 27 Sep 2026 01:12:22 +0200 Subject: [PATCH 3/9] fix(sdk): require a campaign update field --- examples/full-platform/lib/webhooks.ts | 2 +- packages/typescript/src/messaging/types.ts | 12 ++++++++++-- packages/typescript/test/campaigns.test.ts | 11 +++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/examples/full-platform/lib/webhooks.ts b/examples/full-platform/lib/webhooks.ts index fa9020e3..4164f8fa 100644 --- a/examples/full-platform/lib/webhooks.ts +++ b/examples/full-platform/lib/webhooks.ts @@ -25,7 +25,7 @@ export const WEBHOOK_EVENTS = [ "session.logged_out", "template.status", "campaign.completed", - "bansafe.enforcement", + "bansafe.action", "customer.created", ]; diff --git a/packages/typescript/src/messaging/types.ts b/packages/typescript/src/messaging/types.ts index 56ca19af..13d0ca96 100644 --- a/packages/typescript/src/messaging/types.ts +++ b/packages/typescript/src/messaging/types.ts @@ -224,8 +224,7 @@ export interface LaunchCampaignRequest { readonly scheduledAt?: number; } -/** At least one draft field must be supplied; the API rejects an empty object. */ -export interface UpdateCampaignRequest { +interface UpdateCampaignChanges { readonly name?: string; readonly recipientListId?: string | null; readonly senderConfig?: Readonly>; @@ -233,6 +232,15 @@ export interface UpdateCampaignRequest { readonly scheduledAt?: number | null; } +/** At least one draft field must be supplied. */ +export type UpdateCampaignRequest = + | (UpdateCampaignChanges & { readonly name: string }) + | (UpdateCampaignChanges & { readonly recipientListId: string | null }) + | (UpdateCampaignChanges & { + readonly senderConfig: Readonly>; + }) + | (UpdateCampaignChanges & { readonly scheduledAt: number | null }); + export interface RescheduleCampaignRequest { /** Epoch milliseconds, or null to start a waiting campaign now. */ readonly scheduledAt: number | null; diff --git a/packages/typescript/test/campaigns.test.ts b/packages/typescript/test/campaigns.test.ts index c9429412..167deee0 100644 --- a/packages/typescript/test/campaigns.test.ts +++ b/packages/typescript/test/campaigns.test.ts @@ -247,6 +247,17 @@ describe("MessagingClient campaigns", () => { ); }); + it("requires an update field at compile time", () => { + const client = new MessagingClient({ + credential: { type: "apiKey", value: ORGANIZATION_API_KEY }, + }); + const invalidUpdate = () => { + // @ts-expect-error the API rejects an empty campaign update + client.campaigns.update("launch/eu", campaign.id, {}); + }; + expect(invalidUpdate).toBeTypeOf("function"); + }); + it("submits durable lifecycle commands with operation IDs", async () => { const { client, requests } = await campaignsServer(); From 5a3e3a9460842a7ded45268ffc25dff2841a7acd Mon Sep 17 00:00:00 2001 From: Rajeh Taher Date: Sun, 27 Sep 2026 01:19:04 +0200 Subject: [PATCH 4/9] fix(sdk): type Cloud referrals and clarify rescheduling --- packages/typescript/README.md | 28 ++++++++++------- packages/typescript/src/index.ts | 1 + packages/typescript/src/webhooks/events.ts | 10 +++++++ packages/typescript/src/webhooks/index.ts | 1 + .../test/webhook-event-types.test.ts | 30 +++++++++++++++++++ 5 files changed, 60 insertions(+), 10 deletions(-) diff --git a/packages/typescript/README.md b/packages/typescript/README.md index f1736747..8c1614cf 100644 --- a/packages/typescript/README.md +++ b/packages/typescript/README.md @@ -1685,8 +1685,9 @@ message identifiers are URL-encoded by the SDK. ## Messaging campaigns `MessagingClient.campaigns` provides `list`, `create`, `retrieve`, `analytics`, -`listRecipients`, `addRecipients`, `launch`, `pause`, `resume`, `stop`, and -`requeue`. Reads require `campaigns:read`; writes require `campaigns:manage`. +`listRecipients`, `addRecipients`, `update`, `launch`, `reschedule`, `pause`, +`resume`, `stop`, and `requeue`. Reads require `campaigns:read`; writes require +`campaigns:manage`. Pass the project's slug as the first argument. Campaigns accept organization API keys or project tokens; browser client tokens cannot use these methods. @@ -1749,17 +1750,18 @@ They accept the transition without waiting for sending to finish. Stop always cancels; its `operationId` is null when the campaign had no active delivery run and was cancelled immediately. Check for null before calling `Client.operations.wait(operationId)`. A launched campaign waiting for its -scheduled start can be stopped, but its start time cannot be changed. -Launch, pause, resume, and stop generate one idempotency key per call unless you +scheduled start can be stopped or rescheduled. Rescheduling after sending has +started returns `409`. +Launch, reschedule, pause, resume, and stop generate one idempotency key per call unless you pass one. Automatic retries reuse that key; a completed replay returns the API's `idempotency_completed` conflict, so inspect the campaign state after a lost response. `requeue` moves eligible failed recipients, and optionally recipients skipped -with an error, back into the queue. It returns the number moved. The API refuses -unentitled campaigns with `402`, suspension with `403`, and invalid lifecycle -transitions with `409`. Throughput above the eligible number pool's ceiling is -`400 campaign_throughput_capped`. +with an error, back into the queue. It returns the number moved. Launch checks +the selected numbers' Campaigns entitlement and returns `402` when it is absent. +The API refuses suspension with `403` and invalid lifecycle transitions with +`409`. Throughput above a number's ceiling is `400 campaign_throughput_capped`. `Client.campaigns` provides the Platform campaign methods. Its single-campaign reads, updates and deletion take a `PlatformCampaignParams` argument: a team @@ -1769,8 +1771,9 @@ listing and append also require `projectId`. Platform `recipients` uses the same cursor-page shape. `Client.audiences` manages audience members, and `Client.optOuts` reads and replaces team keyword settings. -`create` and `launch` generate an `Idempotency-Key` for each call. A supplied -key is preserved across retries within the API's 24-hour replay window. If the +`launch` and `reschedule` generate an `Idempotency-Key` for each call. Supply a +key for `create` when replay protection is needed. A supplied key is preserved +across retries within the API's 24-hour replay window. If the outcome remains uncertain after that window, reconcile campaign state before starting another request; see [Idempotent sends](#idempotent-sends). `archive` returns a receipt for a completed, failed, or cancelled campaign; @@ -1873,6 +1876,11 @@ WhatsApp Business app on a connected Meta Cloud API number as `{ source: "whatsapp_business_app", value }`. Events for a session created by a QuickLink include its optional `externalId`. +Cloud API `message.received` payloads may include a `referral` object with +`source_type`, `source_id`, `source_url`, and `ctwa_clid` from a Click-to-WhatsApp +entry point. These fields describe the entry point; they do not establish a +conversion or payment. + Development builds also export `CallEndedPayload` and `CallTelemetryPayload`. For `call.ended`, check `from` before reading its identity: it is `null` when the media host disappeared before reporting the caller. The reason is diff --git a/packages/typescript/src/index.ts b/packages/typescript/src/index.ts index 89b3e752..d31a6569 100644 --- a/packages/typescript/src/index.ts +++ b/packages/typescript/src/index.ts @@ -981,6 +981,7 @@ export { type ChatMutePayload, type ChatReadPayload, type CloudMessagePayload, + type CloudMessageReferral, type CloudHistorySyncPayload, type CommandResultPayload, type ContactsSyncPayload, diff --git a/packages/typescript/src/webhooks/events.ts b/packages/typescript/src/webhooks/events.ts index d8d8313d..4d6de017 100644 --- a/packages/typescript/src/webhooks/events.ts +++ b/packages/typescript/src/webhooks/events.ts @@ -179,6 +179,15 @@ export interface LinkedDeviceMessagePayload { export type MessagePayload = LinkedDeviceMessagePayload; +/** Meta-supplied Click-to-WhatsApp entry point; it does not prove a conversion. */ +export interface CloudMessageReferral { + readonly source_type?: string; + readonly source_id?: string; + readonly source_url?: string; + readonly ctwa_clid?: string; + readonly [key: string]: unknown; +} + export interface CloudMessagePayload { readonly id: string; readonly whatsapp_ids: WhatsAppMessageIds; @@ -190,6 +199,7 @@ export interface CloudMessagePayload { readonly senderName?: string; readonly nativeFlowResponse?: NativeFlowResponse; readonly interactive?: Readonly>; + readonly referral?: CloudMessageReferral; readonly [key: string]: unknown; } diff --git a/packages/typescript/src/webhooks/index.ts b/packages/typescript/src/webhooks/index.ts index 47d4c8f3..cce85a83 100644 --- a/packages/typescript/src/webhooks/index.ts +++ b/packages/typescript/src/webhooks/index.ts @@ -46,6 +46,7 @@ export { type ChatReadPayload, type CloudHistorySyncPayload, type CloudMessagePayload, + type CloudMessageReferral, type CommandResultPayload, type ContactOptPayload, type ContactUpdatePayload, diff --git a/packages/typescript/test/webhook-event-types.test.ts b/packages/typescript/test/webhook-event-types.test.ts index b614a30f..f8bca75b 100644 --- a/packages/typescript/test/webhook-event-types.test.ts +++ b/packages/typescript/test/webhook-event-types.test.ts @@ -1,3 +1,4 @@ +import { readFileSync } from "node:fs"; import { describe, expect, expectTypeOf, it } from "vitest"; import type { @@ -21,6 +22,7 @@ import type { ChatMutePayload, ChatReadPayload, CloudMessagePayload, + CloudMessageReferral, CommandResultPayload, ContactsSyncPayload, ContactUpdatePayload, @@ -427,6 +429,34 @@ type ExportedPayloads = { }; describe("webhook event payload types", () => { + it("types Meta Cloud referral fields from the public webhook contract", () => { + expectTypeOf().toEqualTypeOf< + CloudMessageReferral | undefined + >(); + expectTypeOf().toEqualTypeOf< + string | undefined + >(); + const spec = JSON.parse( + readFileSync( + new URL("../../../contracts/openapi.messaging.json", import.meta.url), + "utf8", + ), + ) as { + components: { schemas: { CloudMessagePayload: unknown } }; + }; + const schema = ( + spec.components.schemas.CloudMessagePayload as { + properties: { referral: { properties: Record } }; + } + ).properties.referral; + expect(Object.keys(schema.properties).sort()).toEqual([ + "ctwa_clid", + "source_id", + "source_type", + "source_url", + ]); + }); + it("maps every formerly opaque event family to its contract payload", () => { expectTypeOf().toEqualTypeOf(); expectTypeOf().toEqualTypeOf(); From 43284309528d5599b2c6580be6554106340b116c Mon Sep 17 00:00:00 2001 From: Rajeh Taher Date: Sun, 27 Sep 2026 01:28:11 +0200 Subject: [PATCH 5/9] Avoid replay claims for Platform campaign create --- .../app/api/admin/campaigns/route.ts | 11 ++++---- packages/typescript/README.md | 19 +++++++------ packages/typescript/src/platform/campaigns.ts | 16 +++++++++-- .../test/platform-automation.test.ts | 7 ++--- .../test/platform-campaign-create.test.ts | 28 ++++++++++++++++--- 5 files changed, 56 insertions(+), 25 deletions(-) diff --git a/examples/full-platform/app/api/admin/campaigns/route.ts b/examples/full-platform/app/api/admin/campaigns/route.ts index 3e5486f8..eb4a7af0 100644 --- a/examples/full-platform/app/api/admin/campaigns/route.ts +++ b/examples/full-platform/app/api/admin/campaigns/route.ts @@ -22,12 +22,11 @@ export const POST = route("admin", async ({ body, request }) => { const payload = (body.payload ?? {}) as Body; const name = action(body); if (name === "create") { - return campaigns.create( - { ...payload, name: text(payload, "name"), projectId: env.projectId() }, - { - idempotencyKey: idempotencyKey(request), - }, - ); + return campaigns.create({ + ...payload, + name: text(payload, "name"), + projectId: env.projectId(), + }); } const id = text(body, "campaignId"); const scope = { projectId: env.projectId() }; diff --git a/packages/typescript/README.md b/packages/typescript/README.md index 8c1614cf..6452bd04 100644 --- a/packages/typescript/README.md +++ b/packages/typescript/README.md @@ -1771,19 +1771,22 @@ listing and append also require `projectId`. Platform `recipients` uses the same cursor-page shape. `Client.audiences` manages audience members, and `Client.optOuts` reads and replaces team keyword settings. -`launch` and `reschedule` generate an `Idempotency-Key` for each call. Supply a -key for `create` when replay protection is needed. A supplied key is preserved -across retries within the API's 24-hour replay window. If the -outcome remains uncertain after that window, reconcile campaign state before -starting another request; see [Idempotent sends](#idempotent-sends). +`launch` and `reschedule` generate an `Idempotency-Key` for each call. The +Platform `create` route has no replay contract, so the SDK sends it once and +does not accept an idempotency key or retry setting. After a lost create +response, inspect the campaign list before creating another draft. For launch +and reschedule, a supplied key is preserved across retries within the API's +24-hour replay window. If the outcome remains uncertain after that window, +reconcile campaign state before starting another request; see +[Idempotent sends](#idempotent-sends). `archive` returns a receipt for a completed, failed, or cancelled campaign; other states return `409`. A pending final event or active delivery run also returns `409`; retry after both finish. Platform `delete` accepts draft, completed, failed, cancelled, or archived campaigns. A completed or failed campaign with a pending final event or active delivery run returns `409`. -The Messaging API has no campaign update, deletion, archive, duplicate, or -campaign event history method. The SDK does not substitute Platform routes for -those operations. +The Messaging API has no campaign deletion, archive, duplicate, or campaign +event history method. The SDK does not substitute Platform routes for those +operations. ## Chats diff --git a/packages/typescript/src/platform/campaigns.ts b/packages/typescript/src/platform/campaigns.ts index a64c016f..3b4b9eb5 100644 --- a/packages/typescript/src/platform/campaigns.ts +++ b/packages/typescript/src/platform/campaigns.ts @@ -19,6 +19,10 @@ import type { } from "./types.js"; type CampaignResponse = Promise>>; +type CreateCampaignOptions = Omit< + RequestOptions, + "idempotencyKey" | "maxNetworkRetries" +>; type CampaignAction = "launch" | "pause" | "resume" | "stop" | "archive" | "duplicate" | "requeue"; type CampaignRead = "analytics" | "events"; @@ -45,13 +49,21 @@ export class CampaignsResource { create( body: CreatePlatformCampaignRequest, - options: RequestOptions = {}, + options: CreateCampaignOptions = {}, ): CampaignResponse { return this.transport.request({ method: "POST", path: "/platform/campaigns", body, - ...options, + ...(options.apiVersion === undefined + ? {} + : { apiVersion: options.apiVersion }), + ...(options.headers === undefined ? {} : { headers: options.headers }), + ...(options.signal === undefined ? {} : { signal: options.signal }), + ...(options.timeoutMs === undefined + ? {} + : { timeoutMs: options.timeoutMs }), + maxNetworkRetries: 0, }); } diff --git a/packages/typescript/test/platform-automation.test.ts b/packages/typescript/test/platform-automation.test.ts index f0e52ac6..e7d73588 100644 --- a/packages/typescript/test/platform-automation.test.ts +++ b/packages/typescript/test/platform-automation.test.ts @@ -221,10 +221,7 @@ describe("Client campaigns", () => { projectId: "project/a", projectSlug: "support", }); - await client.campaigns.create( - { projectId: "project/a", name: "August" }, - { idempotencyKey: "campaign-1" }, - ); + await client.campaigns.create({ projectId: "project/a", name: "August" }); await client.campaigns.retrieve("campaign/a", { projectId: "project/a" }); await client.campaigns.update( "campaign/a", @@ -241,7 +238,7 @@ describe("Client campaigns", () => { "DELETE /platform/campaigns/campaign%2Fa?projectId=project%2Fa", ]); expect(requests[1]?.body).toBe('{"projectId":"project/a","name":"August"}'); - expect(requests[1]?.headers["idempotency-key"]).toBe("campaign-1"); + expect(requests[1]?.headers["idempotency-key"]).toBeUndefined(); expect(requests[3]?.body).toBe('{"name":"September"}'); }); diff --git a/packages/typescript/test/platform-campaign-create.test.ts b/packages/typescript/test/platform-campaign-create.test.ts index 58248550..f49cd867 100644 --- a/packages/typescript/test/platform-campaign-create.test.ts +++ b/packages/typescript/test/platform-campaign-create.test.ts @@ -90,6 +90,10 @@ it("requires the organization project and body at compile time", () => { // @ts-expect-error senderConfig is an object, unlike the opaque JSON fields senderConfig: false, }); + // @ts-expect-error the Platform create route has no replay contract + client.campaigns.create(body, { idempotencyKey: "not-supported" }); + // @ts-expect-error the Platform create route must not be retried + client.campaigns.create(body, { maxNetworkRetries: 2 }); }; expect(invalidCalls).toBeTypeOf("function"); }); @@ -107,16 +111,14 @@ it("sends the required scope and preserves every JSON field to the Platform rout credential: { type: "organizationApiKey", value: ORGANIZATION_API_KEY }, baseUrl: server.url, }); - const result = await client.campaigns.create(body, { - idempotencyKey: "create-august", - }); + const result = await client.campaigns.create(body); expect(server.requests).toHaveLength(1); expect(server.requests[0]).toMatchObject({ method: "POST", path: "/platform/campaigns", }); expect(JSON.parse(server.requests[0]!.body)).toEqual(body); - expect(server.requests[0]!.headers["idempotency-key"]).toBe("create-august"); + expect(server.requests[0]!.headers["idempotency-key"]).toBeUndefined(); expect(result.data.data).toEqual({ id: "campaign-1", name: body.name, @@ -124,6 +126,24 @@ it("sends the required scope and preserves every JSON field to the Platform rout }); }); +it("never retries a Platform create after an uncertain server response", async () => { + const server = await startTestServer(() => ({ + status: 503, + headers: { "content-type": "application/json" }, + body: JSON.stringify({ + error: { code: "temporarily_unavailable", message: "Try later" }, + }), + })); + servers.push(server); + const client = new Client({ + credential: { type: "organizationApiKey", value: ORGANIZATION_API_KEY }, + baseUrl: server.url, + maxNetworkRetries: 3, + }); + await expect(client.campaigns.create(body)).rejects.toThrow(); + expect(server.requests).toHaveLength(1); +}); + it("returns the archive receipt and surfaces an active-campaign conflict", async () => { const server = await startTestServer((_request, index) => index === 0 From 87eb35cf523b87bd5e2440db9f27c34818c8382f Mon Sep 17 00:00:00 2001 From: Rajeh Taher Date: Sun, 27 Sep 2026 08:22:12 +0200 Subject: [PATCH 6/9] docs(typescript): scope create idempotency to MessagingClient campaigns --- packages/typescript/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/typescript/README.md b/packages/typescript/README.md index 6452bd04..68712d49 100644 --- a/packages/typescript/README.md +++ b/packages/typescript/README.md @@ -1024,7 +1024,8 @@ These methods send an `Idempotency-Key` on every call: - `messages.send` and `messages.react` - `chats.editMessage` and `chats.deleteMessage` - `channels.reactToMessage` -- `campaigns.create`, `campaigns.launch`, and `campaigns.reschedule` +- `MessagingClient.campaigns.create`, plus `campaigns.launch` and + `campaigns.reschedule` on both clients If you don't pass `idempotencyKey`, the SDK generates a random UUID for the call. Every automatic retry of that call reuses the key, so the API never @@ -2029,7 +2030,6 @@ const campaign = await platform.campaigns.create( name: "August launch", }, { - idempotencyKey: "campaign-august-2026", timeoutMs: 10_000, }, ); @@ -2037,6 +2037,8 @@ const campaign = await platform.campaigns.create( console.log(campaign.data.data, campaign.metadata.requestId); ``` +`Client.campaigns.create` does not accept `idempotencyKey` and never retries +automatically, so a lost response can't create a second campaign. `Client.campaigns.create` requires `CreatePlatformCampaignRequest`, including `name` and the owning `projectId`. It accepts `templateId`, `recipientListId`, `senderConfig`, `scheduledAt`, inline `recipients` (at most 1,000), and From 3c5e13638475d015f3ef8c9ffb592956ec05bb1c Mon Sep 17 00:00:00 2001 From: Rajeh Taher Date: Thu, 1 Oct 2026 00:41:26 +0300 Subject: [PATCH 7/9] Repin SDK contracts to merged API dev commit 5c8014d7d Copy the Messaging and Platform OpenAPI snapshots byte for byte from polymorfa/polymorfa 5c8014d7d58b4d3e483d9d124ee9d88bd85820f9 (PR #388 merged into dev) and reconcile the ledger: 529 operations, 381 covered, 1 partial, 15 missing, 132 excluded, 0 changed. Type campaign send windows, the open PlatformCampaign result, QuickLink reauthorization purpose, hosted-history mediaRetrieval and participant handRaised. --- contracts/coverage.json | 684 +- contracts/openapi.messaging.json | 26162 +++++++++++++--- contracts/openapi.platform.json | 5196 ++- contracts/source.json | 10 +- contracts/testing-events.json | 4 +- packages/typescript/src/index.ts | 6 + packages/typescript/src/messaging/history.ts | 15 + .../typescript/src/messaging/quicklinks.ts | 2 +- packages/typescript/src/messaging/types.ts | 60 +- packages/typescript/src/platform/campaigns.ts | 10 +- packages/typescript/src/platform/types.ts | 19 + packages/typescript/test/campaigns.test.ts | 29 + .../test/coverage-reconciliation.test.ts | 7 +- packages/typescript/test/coverage.test.ts | 18 +- .../test/platform-campaign-create.test.ts | 7 + 15 files changed, 26877 insertions(+), 5352 deletions(-) diff --git a/contracts/coverage.json b/contracts/coverage.json index 6f4b1ecb..aa2983f5 100644 --- a/contracts/coverage.json +++ b/contracts/coverage.json @@ -1,6 +1,6 @@ { "schemaVersion": 1, - "sourceCommit": "2f24c1d62", + "sourceCommit": "5c8014d7d58b4d3e483d9d124ee9d88bd85820f9", "excludedContracts": [ { "family": "graph", @@ -13,7 +13,7 @@ "method": "DELETE", "path": "/messaging/{session}/business/collections/{collectionId}", "operationId": "deleteBusinessCollection", - "fingerprint": "359cc9498cc56b25e610192ad60ce5e9312c127d7f53f117bfa61b66f4547823", + "fingerprint": "04e1889ad25d95965607459cc1a620080bcb3fffe0cfecf069b324b5a5397743", "typescript": { "status": "covered", "method": "MessagingClient.business.deleteCollection" @@ -24,7 +24,7 @@ "method": "DELETE", "path": "/messaging/{session}/business/products/{productId}", "operationId": "deleteBusinessProduct", - "fingerprint": "b22e533c227f7a2cabf2d7e59d1f06d6034834e68bfbf487e2b96ffe86797498", + "fingerprint": "beaf56f49c759122154865a3a0720386f228e6f97cf701c21aaba3d5b73b1c95", "typescript": { "status": "covered", "method": "MessagingClient.business.deleteProduct" @@ -35,7 +35,7 @@ "method": "DELETE", "path": "/messaging/{session}/business/profile/cover-photo/{coverPhotoId}", "operationId": "deleteBusinessCoverPhoto", - "fingerprint": "c6cd9595dc9275fc5ed7347e14e322caedbf18e35027df5ab5f748241626b05a", + "fingerprint": "fc0933524b209678f0b791d97c612bfda39e76536feb269dae591e7baa355371", "typescript": { "status": "covered", "method": "MessagingClient.business.deleteCoverPhoto" @@ -46,7 +46,7 @@ "method": "DELETE", "path": "/messaging/{session}/business/quick-replies/{quickReplyId}", "operationId": "deleteBusinessQuickReply", - "fingerprint": "d6f516e90c1ef08c0d88e118f475371c0b96f34caa268f924a8e17f937ceeef8", + "fingerprint": "99572ef30d31c4df017342c7284f4a883ed960776c14e76ec4c30be613eb6716", "typescript": { "status": "covered", "method": "MessagingClient.quickReplies.delete" @@ -57,7 +57,7 @@ "method": "DELETE", "path": "/messaging/{session}/channels/{id}", "operationId": "deleteChannel", - "fingerprint": "0e985fa6e1f2036a5110f3f434516b7679c804aa5ddb17e5a30d1801356ac6c9", + "fingerprint": "e7aba7ebc75745b23aaa6f8efd7d7afbf8ba43632f4a36da643b4378c5b56c7f", "typescript": { "status": "covered", "method": "MessagingClient.channels.delete" @@ -68,7 +68,7 @@ "method": "DELETE", "path": "/messaging/{session}/chats/{conversation}/messages/{messageId}", "operationId": "deleteMessage", - "fingerprint": "94193b84df0998d21761a3ca5a5a690701a87c758041b26e987ab8781605549e", + "fingerprint": "42a60110a450b80dd1c9d5f52a47e602280157ad81605718f379937e75f2c114", "typescript": { "status": "covered", "method": "MessagingClient.chats.deleteMessage" @@ -79,7 +79,7 @@ "method": "DELETE", "path": "/messaging/{session}/groups/{id}", "operationId": "deleteGroup", - "fingerprint": "71714977bb192d333583ffe7fba9dbbc00bee4b6eec7f83b927fab41f0dbcee5", + "fingerprint": "5deddb9b7890c304c37c8440654369ed558fb06393f69bf43fd869886e983569", "typescript": { "status": "covered", "method": "MessagingClient.groups.delete" @@ -90,7 +90,7 @@ "method": "DELETE", "path": "/messaging/{session}/labels/{labelId}", "operationId": "deleteLabel", - "fingerprint": "bcddc5e8385eb16682751b2073102772fb0790e76c264c49443056e7875fd95f", + "fingerprint": "b7d290447b683c437b764edc357f919d5f43c7f0ddb6ab34ddfc0ade83919956", "typescript": { "status": "covered", "method": "MessagingClient.labels.delete" @@ -101,18 +101,30 @@ "method": "DELETE", "path": "/messaging/{session}/profile/picture", "operationId": "deleteProfilePicture", - "fingerprint": "d9d1df30dc425729ca2622d86163bd63f34dde26a71a6e4a479740438f06f22c", + "fingerprint": "f57bcf6415d916695343df3c5c91a490921f6a0496a53ba152cfc565acb430aa", "typescript": { "status": "covered", "method": "MessagingClient.profile.deletePicture" } }, + { + "family": "messaging", + "method": "DELETE", + "path": "/messaging/{session}/templates/{name}", + "operationId": "deleteTemplate", + "fingerprint": "a74c0a441a2c71001e54b47e516b5dd67d0ef3051a73a3eca29b133e4ea8f55d", + "typescript": { + "status": "missing", + "reason": "No typed Cloud template method on this branch; MessagingClient.cloudTemplates.delete is proposed in SDK PR #308.", + "milestone": "official-api-consumers" + } + }, { "family": "messaging", "method": "DELETE", "path": "/messaging/projects/{projectSlug}/templates/{id}", "operationId": "deleteProjectTemplate", - "fingerprint": "1b1be00f9b7bf5e6a664515b957766389abc6abff0539a82660506d9a41b3ce6", + "fingerprint": "a1825c5f65623505ffac4cad1417eb4c0e4a2ec8039776420d92d6f4db7a6bdc", "typescript": { "status": "covered", "method": "MessagingClient.templates.delete" @@ -123,7 +135,7 @@ "method": "DELETE", "path": "/messaging/quicklinks/{id}", "operationId": "cancelQuickLink", - "fingerprint": "9c0176929aab87ab08edc96635e191d786e20cc1350883d950fbb90c703100db", + "fingerprint": "a0e18de46e67aa0c118274d6a7670562aa6d7252ab2369bc20e18265bdb1dc13", "typescript": { "status": "covered", "method": "MessagingClient.quickLinks.cancel" @@ -134,7 +146,7 @@ "method": "DELETE", "path": "/messaging/voip/calls/{id}", "operationId": "voipTeardown", - "fingerprint": "9e56c6f7db04e1bc6357f9f333bbc9e82877e669ef7ce64ef4744186a76c4b74", + "fingerprint": "c265e5b627970715021366a83804c5ddfc3250be772b5d9f5ca8b724ed2ea2b5", "typescript": { "status": "covered", "method": "MessagingClient.voip.end" @@ -145,7 +157,7 @@ "method": "DELETE", "path": "/messaging/webhooks/{id}", "operationId": "deleteWebhook", - "fingerprint": "42dc1f9c982baa5163313185ad44526260f08297625e636f80e047e62bdb8aa3", + "fingerprint": "fa386b4afac45ee26eff6db68a8cae298f96d4e494947a11dcaf95d3341d6869", "typescript": { "status": "covered", "method": "MessagingClient.webhooks.delete" @@ -167,7 +179,7 @@ "method": "GET", "path": "/messaging/{session}/business/catalog", "operationId": "getBusinessCatalog", - "fingerprint": "0343669a80027d2b3a15ffa36b247fa64dae908dd6e4d44daff1885404aa2e7b", + "fingerprint": "4b028c82d72efdf7b5f124d5966e66ba3a3d24bb8fcdc794a821fc87bf071747", "typescript": { "status": "covered", "method": "MessagingClient.business.getCatalog" @@ -178,7 +190,7 @@ "method": "GET", "path": "/messaging/{session}/business/collections", "operationId": "getBusinessCollections", - "fingerprint": "d8dd52927e0d82edc771a4dc5e77876cd0556622ceffc58abdad71d13a78728d", + "fingerprint": "66915d9493e15cb1c06648015b2adbbd2fa68530c1fb13e21362ef3245d81fad", "typescript": { "status": "covered", "method": "MessagingClient.business.listCollections" @@ -189,7 +201,7 @@ "method": "GET", "path": "/messaging/{session}/business/collections/{collectionId}", "operationId": "getBusinessCollection", - "fingerprint": "b5a5c7f941eb9f6ff9040dcf89ca94166f933415af1cf55dde784098d00ec26c", + "fingerprint": "2abd535e097ef20c80858e6e17131c0969f8328949e8b1d72af6f6f242ddacce", "typescript": { "status": "covered", "method": "MessagingClient.business.getCollection" @@ -200,7 +212,7 @@ "method": "GET", "path": "/messaging/{session}/business/compliance", "operationId": "getBusinessMerchantCompliance", - "fingerprint": "b6fbd1133265954a4108b4a222e9fa4128ac98e06327a12a092ff210c8070c3b", + "fingerprint": "ab6127c69880a3eecdcf4e4f7c514f0936ff87f500b1114bd48966d856e83894", "typescript": { "status": "covered", "method": "MessagingClient.business.getMerchantCompliance" @@ -211,7 +223,7 @@ "method": "GET", "path": "/messaging/{session}/business/eligibility", "operationId": "getBusinessEligibility", - "fingerprint": "1b040b988e1c1ba072a3e83be4482b4078bffa1e273a4ae72222d0c67675af1f", + "fingerprint": "7324f4925979abc8e286083f597b334fb17a4632df78dc8115b28d48b9269900", "typescript": { "status": "covered", "method": "MessagingClient.business.getEligibility" @@ -222,7 +234,7 @@ "method": "GET", "path": "/messaging/{session}/business/linked-accounts", "operationId": "getBusinessLinkedAccounts", - "fingerprint": "e0dbebc8e3df452862769a0d20e44d939f576143cc70f4f03a97e62bfbc0da39", + "fingerprint": "7af1b8a871b4e31ec05bd6390a21ff9df12035eaeb136a4e5452527c854d1dd0", "typescript": { "status": "covered", "method": "MessagingClient.business.getLinkedAccounts" @@ -233,7 +245,7 @@ "method": "GET", "path": "/messaging/{session}/business/products/{productId}", "operationId": "getBusinessProduct", - "fingerprint": "f8f826f843688fbdb9145870cd23dec351d886d45f46110531edda81ba01fd3c", + "fingerprint": "474c7fcc054cc967cd66f6f48310ea0cdefcfa3f62fbc6907e3d4aede7485826", "typescript": { "status": "covered", "method": "MessagingClient.business.getProduct" @@ -244,7 +256,7 @@ "method": "GET", "path": "/messaging/{session}/business/profile", "operationId": "getOwnBusinessProfile", - "fingerprint": "a2d6752596ac3949ce4a58d0a61e925e694043f93e7c473451a2c117acdd154e", + "fingerprint": "3ff6c0b1cd656adbc9951f0d5cc5e2f7c326018cd0c753dc3d59af1eb4d42675", "typescript": { "status": "covered", "method": "MessagingClient.business.getProfile" @@ -255,7 +267,7 @@ "method": "GET", "path": "/messaging/{session}/business/quick-replies", "operationId": "listBusinessQuickReplies", - "fingerprint": "bd9c3cab1c8cad3a8681ccea4b5394f190fa3a36c6c72edb8991ecfbfba11623", + "fingerprint": "e45669ffde440117c22e10abca733315804ddd727b64c379ff3c5152ed44533c", "typescript": { "status": "covered", "method": "MessagingClient.quickReplies.list" @@ -266,7 +278,7 @@ "method": "GET", "path": "/messaging/{session}/call-permissions/{to}", "operationId": "getCallPermission", - "fingerprint": "7ddf19fca942e2cb1bb6238002f25befd40bb09527e28d8be8b43d80f82130cf", + "fingerprint": "abe395419cf56042684d42364c9ad847a27c2b63f8e51cc68a4d192b079dab7f", "typescript": { "status": "covered", "method": "MessagingClient.voip.retrieveCallPermission" @@ -277,7 +289,7 @@ "method": "GET", "path": "/messaging/{session}/channels", "operationId": "listChannels", - "fingerprint": "69c7395ff270a61853006079f6ea13d9cbe5d56077961ea4a906d52f6d2c8c1f", + "fingerprint": "26e8dfd6d9accad2c70ae0eab1788c05d2efe1d1fdb958d38c9c82923d02b2c9", "typescript": { "status": "covered", "method": "MessagingClient.channels.list" @@ -288,7 +300,7 @@ "method": "GET", "path": "/messaging/{session}/channels/{id}", "operationId": "getChannel", - "fingerprint": "47a3626b8efe58e590dcffafced6cde94dd5a195d7fc2c302809d6fa4224f2bc", + "fingerprint": "7ebbdadf08ee600ababfe8b4b60070493c710903aa61f62112fd4589f6ae27e0", "typescript": { "status": "covered", "method": "MessagingClient.channels.retrieve" @@ -299,7 +311,7 @@ "method": "GET", "path": "/messaging/{session}/channels/{id}/message-updates", "operationId": "listChannelMessageUpdates", - "fingerprint": "d0a8540ab7d9d475638a1f30a3ab8a2e4b292a72b12d4c373375461e07c8a110", + "fingerprint": "1d2fc7c66494c85d23c8a857f035ffb34444b06146f1f5f8a91b1b6988d5c9c0", "typescript": { "status": "covered", "method": "MessagingClient.channels.listMessageUpdates" @@ -310,7 +322,7 @@ "method": "GET", "path": "/messaging/{session}/channels/{id}/messages", "operationId": "listChannelMessages", - "fingerprint": "4435249048d4acb36667bd7e14ac2d7a8d17b06059aa8ca3610e9d3794b4d646", + "fingerprint": "f336f08a04542829b894ef9b4ab09816eec467fb70484e83db29661280ccd294", "typescript": { "status": "covered", "method": "MessagingClient.channels.listMessages" @@ -321,7 +333,7 @@ "method": "GET", "path": "/messaging/{session}/chats", "operationId": "listChats", - "fingerprint": "efb75aef9b2cd4349d3627680dcfb5215958c700413abc233af7406dacf743ed", + "fingerprint": "c65cc82eaa1db61bda6c6d59b7e69ea4a28e7f65a128b8225b4f3601b9197954", "typescript": { "status": "covered", "method": "MessagingClient.chats.list" @@ -332,7 +344,7 @@ "method": "GET", "path": "/messaging/{session}/chats/{conversation}", "operationId": "getChat", - "fingerprint": "4c7093395b06165fa3de12f0855fe07bfb0d613ac35e792e878093b9dada7768", + "fingerprint": "37e1926f60fc2def5fc732c7ac8b4b8e9fac2bee76ad8786c80d62e2b5eece9e", "typescript": { "status": "covered", "method": "MessagingClient.chats.retrieve" @@ -343,7 +355,7 @@ "method": "GET", "path": "/messaging/{session}/chats/{conversation}/messages", "operationId": "listChatMessages", - "fingerprint": "eedca694e77953c840f19e0999be9aafd3f4ec67c08ec6c6653e4d31debc9a57", + "fingerprint": "6f1c4f7a6ec9455ca3920d694b785854dd404fa3345898e9aa52325945261935", "typescript": { "status": "covered", "method": "MessagingClient.chats.listMessages" @@ -354,18 +366,54 @@ "method": "GET", "path": "/messaging/{session}/chats/{conversation}/messages/{messageId}", "operationId": "getChatMessage", - "fingerprint": "40dadd9f752d0fe5231120f5ebc385751bb8d8eaf228070581055ba5c94e6987", + "fingerprint": "5750ed93a4031765ec9abae8fbfdfe6ad94cf7c2060f4f427e1f08bd8b151b6e", "typescript": { "status": "covered", "method": "MessagingClient.chats.retrieveMessage" } }, + { + "family": "messaging", + "method": "GET", + "path": "/messaging/{session}/chats/{conversation}/messages/{messageId}/media", + "operationId": "getChatMessageMedia", + "fingerprint": "104d13ee3a51a7c02d4d78aac2f1d98bbe6967b7e60314aab1f0d5a84e9041e2", + "typescript": { + "status": "missing", + "reason": "Hosted-history media download has no typed method on this branch; it is proposed in SDK PR #316.", + "milestone": "official-api-consumers" + } + }, + { + "family": "messaging", + "method": "GET", + "path": "/messaging/{session}/chats/{conversation}/service-window", + "operationId": "getCustomerServiceWindow", + "fingerprint": "41682e6f4f46cef3a31bd5e24ab9502040d20796c8bb31656f76823b21f3e735", + "typescript": { + "status": "missing", + "reason": "No typed Official API service-window read in the SDK yet.", + "milestone": "official-api-consumers" + } + }, + { + "family": "messaging", + "method": "GET", + "path": "/messaging/{session}/cloud-credentials", + "operationId": "getCloudCredentialHealth", + "fingerprint": "0a88502f94d641851cb60e4529306868b5bb4d6f2e350a0a9f8600b0965ef0cc", + "typescript": { + "status": "missing", + "reason": "No typed Official API credential-health read in the SDK yet.", + "milestone": "official-api-consumers" + } + }, { "family": "messaging", "method": "GET", "path": "/messaging/{session}/contacts", "operationId": "listContacts", - "fingerprint": "97c8ed2eefae451c08722ecfa04018239943371732f53e79a846275eb3f9cdbb", + "fingerprint": "4cb6cee21d463dadffd95e238acd8087a767092a15064d0f358c6cc04f5adca0", "typescript": { "status": "covered", "method": "MessagingClient.contacts.list" @@ -376,7 +424,7 @@ "method": "GET", "path": "/messaging/{session}/contacts/{contactId}", "operationId": "getContact", - "fingerprint": "630edd70de0a1812b2904db8562455c2df2225eda80d04b845f0b08e9ddb6be7", + "fingerprint": "6a997c25d60dc0c9183f43e7deaaff17f0241ca7861f24a25e21f6cf7a7e5f1e", "typescript": { "status": "covered", "method": "MessagingClient.contacts.retrieve" @@ -387,7 +435,7 @@ "method": "GET", "path": "/messaging/{session}/contacts/{contactId}/business-profile", "operationId": "getBusinessProfile", - "fingerprint": "6243cac1a451121d42001d57877e51abba54c452d1c8c57b21225ec61a303bba", + "fingerprint": "88ed455c0a01899322f6080800951c53278521b7691b18dbacfa799f7e368c9d", "typescript": { "status": "covered", "method": "MessagingClient.contacts.businessProfile" @@ -398,7 +446,7 @@ "method": "GET", "path": "/messaging/{session}/contacts/{contactId}/devices", "operationId": "getUserDevices", - "fingerprint": "6653eed239848a27d05b35a971bb050737e418c5f818bacedd2e86a035126bda", + "fingerprint": "9e78b116872536daa51fa2ed94be468ff8f57e49c94ccef1f232c3b31d00dceb", "typescript": { "status": "covered", "method": "MessagingClient.contacts.devices" @@ -409,7 +457,7 @@ "method": "GET", "path": "/messaging/{session}/contacts/{contactId}/info", "operationId": "getUserInfo", - "fingerprint": "90653344655bcd6533e4262223c9eb94679e7490f7d6672df3808c31f528c6a2", + "fingerprint": "f7ec95a1840cb01179c3f45bc0d73ab9975421225bae7c5e8581472b60a98d26", "typescript": { "status": "covered", "method": "MessagingClient.contacts.info" @@ -420,7 +468,7 @@ "method": "GET", "path": "/messaging/{session}/contacts/{contactId}/picture", "operationId": "getContactPicture", - "fingerprint": "447e93829c67b47b5a5ef5a1f3dbe6428db0e985743a3f49627597190ef3725f", + "fingerprint": "cf218273d8bd89832a43f7d5fed982180886e22ebff91b2d8bad19b938a9d1ba", "typescript": { "status": "covered", "method": "MessagingClient.contacts.picture" @@ -431,7 +479,7 @@ "method": "GET", "path": "/messaging/{session}/contacts/blocked", "operationId": "getBlocklist", - "fingerprint": "827304a10677b72d262692ef64e13c37f86ea3792c944ff8bf3c96640674e21d", + "fingerprint": "2968c884a8f5776faf83014d881995a9e55b010682768252fd71ec9feead02cb", "typescript": { "status": "covered", "method": "MessagingClient.contacts.blocklist" @@ -442,7 +490,7 @@ "method": "GET", "path": "/messaging/{session}/contacts/check", "operationId": "checkContacts", - "fingerprint": "41d3f2a9f031820d2535e65e1c1556bf63e107d78b8dc8defa1560c098996f19", + "fingerprint": "b360734d8c23e417321fca1b66d5d048aa7b4312c31ca70c1968d02c16b9f419", "typescript": { "status": "covered", "method": "MessagingClient.contacts.check" @@ -453,7 +501,7 @@ "method": "GET", "path": "/messaging/{session}/groups", "operationId": "listGroups", - "fingerprint": "557fdaae3fd9c9b5053f9a7ca03741232f446415ba0a8372f38744eca263ee76", + "fingerprint": "1eddeecf37f30a1b82b601a6e1882d6923ad58c4831b2539674e1f876ea9235d", "typescript": { "status": "covered", "method": "MessagingClient.groups.list" @@ -464,7 +512,7 @@ "method": "GET", "path": "/messaging/{session}/groups/{id}", "operationId": "getGroup", - "fingerprint": "051c41c85fff332132442add73be7d97d48690de74099bee5589343d0e0fea69", + "fingerprint": "2d64ebde1727251bd925ded38afd74a65306ef45a8f843a196b36db6b2bfc5cf", "typescript": { "status": "covered", "method": "MessagingClient.groups.retrieve" @@ -475,7 +523,7 @@ "method": "GET", "path": "/messaging/{session}/groups/{id}/invite-code", "operationId": "getGroupInviteCode", - "fingerprint": "a9dcd0a74eb65a8acae7b75e0f8b921b292343a1fdd054965195fc04afa6ece7", + "fingerprint": "9c66af266c2cb0c4b0874d13a9a2e561a22d505b54532830850e17c5cc68fb1d", "typescript": { "status": "covered", "method": "MessagingClient.groups.getInviteCode" @@ -486,7 +534,7 @@ "method": "GET", "path": "/messaging/{session}/groups/{id}/participants", "operationId": "getGroupParticipants", - "fingerprint": "85d01079ab4c01f365f1a09a3d95fb47dab7b8f9c18438b2eb566f6dd6ddceb0", + "fingerprint": "a9ade07b491c936407298b4afdb8e9e5d1cb3d6167e4f7d6bdf27d4e0fca6354", "typescript": { "status": "covered", "method": "MessagingClient.groups.listParticipants" @@ -497,7 +545,7 @@ "method": "GET", "path": "/messaging/{session}/groups/join-info", "operationId": "getGroupJoinInfo", - "fingerprint": "df9e90196753b285abe9cd2220f8b9c52070cae3803739eb3ba2f54c11089518", + "fingerprint": "8513fad6b5bb2767162dcaef70f3df0f4532e73c1cfa871d5eef9d026293cf58", "typescript": { "status": "covered", "method": "MessagingClient.groups.getJoinInfo" @@ -508,7 +556,7 @@ "method": "GET", "path": "/messaging/{session}/hybrid-link", "operationId": "getHybridLinkState", - "fingerprint": "5856d04e61b223199fdc4577aaba39da07b0692c88466d95b99b03217e44570c", + "fingerprint": "8015be522203f5baba6cfb7cf5b1d8634a13c301e77c57e64769c636b1189ab7", "typescript": { "status": "covered", "method": "MessagingClient.hybridLink.state" @@ -519,7 +567,7 @@ "method": "GET", "path": "/messaging/{session}/identities/resolve", "operationId": "resolveIdentity", - "fingerprint": "b4f5f2df9f32dc446dd03f1ef2ebe2a08d8b54e9136c2d3eabb4dc2777054e7a", + "fingerprint": "19ed63f8b68035fe173f8067a589c77f1a392f7344e0313484837ed625a87ffe", "typescript": { "status": "covered", "method": "MessagingClient.identities.resolve" @@ -530,7 +578,7 @@ "method": "GET", "path": "/messaging/{session}/labels", "operationId": "listLabels", - "fingerprint": "62b43c0f75936f3218a2905eba98d4ebd4ba7cb1ba073e6760dfbfe19349ea00", + "fingerprint": "fa396c3d8b54f104fff92f2d9774c8bbb2111c56a4b2c4c154e5a177b225400e", "typescript": { "status": "covered", "method": "MessagingClient.labels.list" @@ -541,18 +589,30 @@ "method": "GET", "path": "/messaging/{session}/labels/chats/{conversation}", "operationId": "getChatLabels", - "fingerprint": "9c1718d2c9779681a1477f805b395f04fc39a044a8d9721858db68b434d84176", + "fingerprint": "09534783dfcbda279d9db079f21fd4475dc7a4ca570d8533b9ecf54d2ad6ecff", "typescript": { "status": "covered", "method": "MessagingClient.labels.listForChat" } }, + { + "family": "messaging", + "method": "GET", + "path": "/messaging/{session}/meta-pricing", + "operationId": "getMetaPricingSummary", + "fingerprint": "c6226fb97a62f7f0b596fe98ad740197a33a6a6e470f03e58ce6b89eff784eca", + "typescript": { + "status": "missing", + "reason": "No typed Official API pricing summary read in the SDK yet.", + "milestone": "official-api-consumers" + } + }, { "family": "messaging", "method": "GET", "path": "/messaging/{session}/observation-policy", "operationId": "getSessionObservationPolicy", - "fingerprint": "f682652452cd459b275c58d4702fa8451f3fe1e86a4d2c90150fc22d8af52c3e", + "fingerprint": "9703eddbd2c3e906169b627e2166dece650dc273705dfb5d0dacf6d428adf063", "typescript": { "status": "covered", "method": "MessagingClient.observationPolicies.retrieveForSession" @@ -563,7 +623,7 @@ "method": "GET", "path": "/messaging/{session}/operations/{operationId}", "operationId": "getMessageOperation", - "fingerprint": "6cd745f7d7be9072271658936369ec0fac1a3db6d35f75d2feb603772b39a520", + "fingerprint": "7d3d1b4b8a1b76ebcc5bb6c4ad9356d51edfb339d45d9736cfabdd6e39d58ad0", "typescript": { "status": "covered", "method": "MessagingClient.messages.operationStatus" @@ -574,7 +634,7 @@ "method": "GET", "path": "/messaging/{session}/pair/qr", "operationId": "getQRCode", - "fingerprint": "50fd6b66daa47375025cb30f122aeaccf996335d4e8ad6f29256dc31c8fdcf35", + "fingerprint": "e714d8085bf6016a19c34cd95d8d950c10936c718dc05745e358b624971ad343", "typescript": { "status": "covered", "method": "MessagingClient.sessions.qr" @@ -585,7 +645,7 @@ "method": "GET", "path": "/messaging/{session}/presence", "operationId": "getPresence", - "fingerprint": "92e889a548d108d46b9e5300dfec1216dc816d5ccaf44eb5f6f2817ab8ee13c9", + "fingerprint": "f404f6a6ab8a4f7eebfd2e0fc2080b1e6d794625dfdacd9ab86a38f078e5ec23", "typescript": { "status": "covered", "method": "MessagingClient.presence.get" @@ -596,7 +656,7 @@ "method": "GET", "path": "/messaging/{session}/presence/{conversation}", "operationId": "getChatPresence", - "fingerprint": "3477e117477fb53842d89c3076e157c4947cd8a1fab509d3677496e5b8407f1b", + "fingerprint": "71bf612340e099a74aea2a3de76e6664c4ec5070e6d4ce1c26950c325e41c4fc", "typescript": { "status": "covered", "method": "MessagingClient.presence.getForChat" @@ -607,7 +667,7 @@ "method": "GET", "path": "/messaging/{session}/privacy", "operationId": "getPrivacySettings", - "fingerprint": "ea2f0ab02f5669ce4971ba218cd8b247449901c283d2cd22318cd5472c01ce1a", + "fingerprint": "a8bf47dfd5750e904d3bf61ae4d1b4dd8d3afebb83d7e1a1cb741c9251ce6381", "typescript": { "status": "covered", "method": "MessagingClient.privacy.get" @@ -618,7 +678,7 @@ "method": "GET", "path": "/messaging/{session}/profile", "operationId": "getProfile", - "fingerprint": "2d179fe40f833323b19fa33fb773822ab239973a913c91c368883e04ae426799", + "fingerprint": "e8723617f71b1502c544b6b236b171188b1c617fe682b1587839a879b6a4f11c", "typescript": { "status": "covered", "method": "MessagingClient.profile.get" @@ -629,18 +689,42 @@ "method": "GET", "path": "/messaging/{session}/safe-mode", "operationId": "getSessionSafeMode", - "fingerprint": "13d0467c725ae1eb95694355d55891ac9a186ab4de5fec97facfc17e759c010e", + "fingerprint": "7b3ca30d739e56c006888bda35c3cf8ce20f26cc02769c5b2ea7579a0d79c8b0", "typescript": { "status": "covered", "method": "MessagingClient.banSafe.getSessionSafeMode" } }, + { + "family": "messaging", + "method": "GET", + "path": "/messaging/{session}/templates", + "operationId": "listTemplates", + "fingerprint": "0e4fb03a957b63354c8c50d6046529f444b6093d8a1a61857975e21ea49ab320", + "typescript": { + "status": "missing", + "reason": "No typed Cloud template method on this branch; MessagingClient.cloudTemplates.list is proposed in SDK PR #308.", + "milestone": "official-api-consumers" + } + }, + { + "family": "messaging", + "method": "GET", + "path": "/messaging/{session}/templates/{name}", + "operationId": "getTemplate", + "fingerprint": "8d222acf68c34a5173c19b8869eb14595896d39f801031e1e5af913f908ecc0a", + "typescript": { + "status": "missing", + "reason": "No typed Cloud template method on this branch; MessagingClient.cloudTemplates.retrieve is proposed in SDK PR #308.", + "milestone": "official-api-consumers" + } + }, { "family": "messaging", "method": "GET", "path": "/messaging/{session}/users/{id}/security-code", "operationId": "getUserSecurityCode", - "fingerprint": "e92fdd0e50a078e31049d6a9fd25ba6cc313c0e19a454281f9adcc768e6b6a07", + "fingerprint": "05f77b314d25402f9096dc3d710fde88867829582ac70be9bb5bb41653202a0f", "typescript": { "status": "covered", "method": "MessagingClient.users.getSecurityCode" @@ -651,7 +735,7 @@ "method": "GET", "path": "/messaging/bridge/route", "operationId": "getBridgeRoute", - "fingerprint": "d321c70c2e126a58784a01def440996c3d756569bcbcc7681c714928eecbb28f", + "fingerprint": "90d48857b0e16577daf8091e4edbb31b758ec37123bd2149085d8ed197f17f70", "typescript": { "status": "covered", "method": "BridgeClient.routes.resolve" @@ -662,7 +746,7 @@ "method": "GET", "path": "/messaging/info/status", "operationId": "getStatus", - "fingerprint": "155d24e33875fedebe35c630ab095a27d0237ea9777e2bf059b51dc542a6bb73", + "fingerprint": "23338dd706e18d3d253e5c2306a188a4228b546fe8166c9c5baf252077c7162b", "typescript": { "status": "covered", "method": "SystemClient.status" @@ -673,7 +757,7 @@ "method": "GET", "path": "/messaging/info/version", "operationId": "getVersion", - "fingerprint": "44ef279481ab1a26863bbb18cb45024cb02b31fcf1dbfb989f8bb4c6b00137a4", + "fingerprint": "cc8bde27daeb5d13ca1b720781867dd26c8bc148e29ad8f8136f04cf8545fe1d", "typescript": { "status": "covered", "method": "SystemClient.version" @@ -684,7 +768,7 @@ "method": "GET", "path": "/messaging/media/{id}", "operationId": "downloadMedia", - "fingerprint": "adc79c6f62c2a11bc0fa37ac118616a60257012b11311fe6cb5ab0132b9b528f", + "fingerprint": "81a26687dc65ecdeb50f02c4471e184efd04968f2f03b21e0b53e841b86187ce", "typescript": { "status": "covered", "method": "MessagingClient.media.download" @@ -695,7 +779,7 @@ "method": "GET", "path": "/messaging/media/{id}/info", "operationId": "getMediaInfo", - "fingerprint": "9bccf0601b961098d8c725210f43936a1c7ce9790db5f22e1f7adf06d7f02c13", + "fingerprint": "32a4fccb471153925d5a128940ed39d13054a518c54c2bf5f907e582f93a37a7", "typescript": { "status": "covered", "method": "MessagingClient.media.retrieve" @@ -706,7 +790,7 @@ "method": "GET", "path": "/messaging/projects/{projectId}/health-policy", "operationId": "getProjectHealthPolicy", - "fingerprint": "8afc3e8389aaaf89b7403647becc6816fb057f4b5f526a6188becdcbc4fe29f1", + "fingerprint": "4b0ff668e5b9761656ad449d59c3d5871bc4a64d09d71b004d9124c78d8ecaea", "typescript": { "status": "covered", "method": "MessagingClient.banSafe.getProjectHealthPolicy" @@ -717,7 +801,7 @@ "method": "GET", "path": "/messaging/projects/{projectId}/insurance-evidence", "operationId": "getProjectInsuranceEvidence", - "fingerprint": "d79ac812a1f9333fd529622287f73225805280fe48830672d7649b3385b109b0", + "fingerprint": "bef5e0332a26a50358dc05d68633077bdc5b40ed06a7bbb2932e159fdeaeae79", "typescript": { "status": "covered", "method": "MessagingClient.banSafe.getProjectInsuranceEvidence" @@ -728,7 +812,7 @@ "method": "GET", "path": "/messaging/projects/{projectId}/observation-policy", "operationId": "getProjectObservationPolicy", - "fingerprint": "611d31d626aefb74eba190298af0b5212beb954851a5094f95d28369af6ebcc5", + "fingerprint": "857cca03f94f3b01e56fa728f7b311e0c7b82d4ae5e071c20bcf41671256f686", "typescript": { "status": "covered", "method": "MessagingClient.observationPolicies.retrieveForProject" @@ -739,7 +823,7 @@ "method": "GET", "path": "/messaging/projects/{projectId}/safe-mode", "operationId": "getProjectSafeMode", - "fingerprint": "5eaf19a1bdd63c685ca5cb270c23f67cac02dbcaa674c0d1ca3c7a55553f1148", + "fingerprint": "8ef655d9cc8e4b3a01d96bd829fcdb752c3f7e5dc5a0c0871b7af05e3fb679c8", "typescript": { "status": "covered", "method": "MessagingClient.banSafe.getProjectSafeMode" @@ -750,7 +834,7 @@ "method": "GET", "path": "/messaging/projects/{projectId}/warmup-plan", "operationId": "getProjectWarmupPlan", - "fingerprint": "dc6009d92678ff376975e8627dbdc1e6def436cbd043e548ecd73568cd59e59c", + "fingerprint": "ca1d5a059388f03caa4f3074a2f65e52ba031c84b11d288dd1a5e3b49e1aaf5f", "typescript": { "status": "covered", "method": "MessagingClient.banSafe.getProjectWarmupPlan" @@ -761,7 +845,7 @@ "method": "GET", "path": "/messaging/projects/{projectSlug}/campaigns", "operationId": "listCampaigns", - "fingerprint": "1534749a9e881ebec81bc42e58586f1224b9cc854dc8f39bba50bb59bb383c76", + "fingerprint": "f791c529a630848b647c40e3ac554ac586c567be9d5fa34c0eb53ac79b910d24", "typescript": { "status": "covered", "method": "MessagingClient.campaigns.list" @@ -772,7 +856,7 @@ "method": "GET", "path": "/messaging/projects/{projectSlug}/campaigns/{id}", "operationId": "getCampaign", - "fingerprint": "df0afb657e91437629b726fc004b1c769343ee277d1dcbe235955d6dc1432d9b", + "fingerprint": "6339320aba7f83fe0749363e292fb842f9fe3bd7d32e0aaa26f82a4c26f7aebf", "typescript": { "status": "covered", "method": "MessagingClient.campaigns.retrieve" @@ -783,7 +867,7 @@ "method": "GET", "path": "/messaging/projects/{projectSlug}/campaigns/{id}/analytics", "operationId": "getCampaignAnalytics", - "fingerprint": "7060015a6c91559b6ae6a0b7e8212219ddaf4c00675e0f9c3fbb7c3ceccdb00b", + "fingerprint": "33f9b1fe922928e2ba422f8fc177467420f0f3f7fc984234aa4e66ef65f7ed45", "typescript": { "status": "covered", "method": "MessagingClient.campaigns.analytics" @@ -794,7 +878,7 @@ "method": "GET", "path": "/messaging/projects/{projectSlug}/campaigns/{id}/recipients", "operationId": "listProjectCampaignRecipients", - "fingerprint": "1da5e8f138bd3870846848812374cf30e2da251a12e27a7c259e0f3e16a56730", + "fingerprint": "c70f5c132a77931274c9557dc5f8a86aed19c4874e99aca0dafb5cddff87fb2d", "typescript": { "status": "covered", "method": "MessagingClient.campaigns.listRecipients" @@ -805,7 +889,7 @@ "method": "GET", "path": "/messaging/projects/{projectSlug}/templates", "operationId": "listProjectTemplates", - "fingerprint": "3f2c770aa94cb0bb961a8b186008e8c79a0beb406377772ab081afa13b954dd6", + "fingerprint": "bec648f1f0eaa9b69feeceab915c412c946515ed935010e01e799d3e17be6a3c", "typescript": { "status": "covered", "method": "MessagingClient.templates.list" @@ -816,7 +900,7 @@ "method": "GET", "path": "/messaging/projects/{projectSlug}/templates/{id}", "operationId": "getProjectTemplate", - "fingerprint": "e8ea66776eb1e7b2c1150a0dc2983595a72d5e967d5ab635a002bbac09b2c5d8", + "fingerprint": "d5bf02a5548b3d1939cbce08866c81f78b1dbe53b020e8b5d844523a56b91178", "typescript": { "status": "covered", "method": "MessagingClient.templates.retrieve" @@ -827,7 +911,7 @@ "method": "GET", "path": "/messaging/quicklinks/{id}", "operationId": "getQuickLink", - "fingerprint": "ec5b9d70c6fa59f17906ad1c2691792167238971734260c354d0dc5e24e7675d", + "fingerprint": "d2ebb8727ef3771a80e936a3672dfc4c41e3adc634911e46f1fce541056ca552", "typescript": { "status": "covered", "method": "MessagingClient.quickLinks.retrieve" @@ -838,7 +922,7 @@ "method": "GET", "path": "/messaging/quicklinks/availability", "operationId": "getHybridQuickLinkAvailability", - "fingerprint": "6cab8df0433d215f00bb441220ced92b53cf2989fefec133041a87b02e70b300", + "fingerprint": "32b9f75cd57d56b7e2d99d17231123327941802b74da52cf688a90af354d5a3c", "typescript": { "status": "covered", "method": "MessagingClient.quickLinks.availability" @@ -849,7 +933,7 @@ "method": "GET", "path": "/messaging/routing/hybrid", "operationId": "getHybridRoutingPolicy", - "fingerprint": "7f61cfbe908fbe8a1efd4f03c3ec185233b138bcb54c0ad82aab769a751d4e1a", + "fingerprint": "8327d47763e76639fdf75c843b5c29bcd3f2afe668f4ef9d6c4264590fdadeaa", "typescript": { "status": "covered", "method": "MessagingClient.hybridLink.getPolicy" @@ -860,7 +944,7 @@ "method": "GET", "path": "/messaging/testing/{projectId}/events/fixtures", "operationId": "listTestEventFixtures", - "fingerprint": "05b7145174ef35215dd9b6f51b83927279d12a1281e4f142f882e13c27fb5361", + "fingerprint": "63fdcfa84f9827b1b4d94d0c8d217b80c88586e864812b63fe84d3af18f2739d", "typescript": { "status": "covered", "method": "MessagingClient.testing.listEventFixtures" @@ -871,7 +955,7 @@ "method": "GET", "path": "/messaging/voip/calls/{id}/candidates", "operationId": "voipCandidates", - "fingerprint": "74f0e421efb2e1a53eaf7f7001161e13b43977b5aa452420955a2b3c02cb1aef", + "fingerprint": "3129d2864557a68019aa71326b1048e80a6f9d9152d2ced91d4a2142e30048fe", "typescript": { "status": "excluded", "reason": "used internally by the Calls SDK; not part of the public API" @@ -882,7 +966,7 @@ "method": "GET", "path": "/messaging/webhooks", "operationId": "listWebhooks", - "fingerprint": "4e9ad674048dc686894889047b19d1bb23619ccb912b0e70c6cc0489c37b6c87", + "fingerprint": "699da1df54959a7dac6c4684d79cf9b335c0dcd9bf0ab79949f7199c234311ac", "typescript": { "status": "covered", "method": "MessagingClient.webhooks.list" @@ -893,7 +977,7 @@ "method": "GET", "path": "/messaging/webhooks/{id}", "operationId": "getWebhook", - "fingerprint": "d8c7a12bca9355b3efaf3dcc7d144718bd7ffcde0afeb1969890e92cde2ae6af", + "fingerprint": "48f761a0705080c50b91a805f73964bc949edc293a79d3f7bfc716f951895f70", "typescript": { "status": "covered", "method": "MessagingClient.webhooks.retrieve" @@ -915,7 +999,7 @@ "method": "GET", "path": "/quicklink/{token}", "operationId": "getQuickLinkState", - "fingerprint": "a462c6d28d81528102959bf45b83a44469433cf1b920a358a8d8a483b82620fa", + "fingerprint": "e26e9fe8bf1eb1f22c26c69007800aff5cd17aefd3985712b1d779a23ee412af", "typescript": { "status": "excluded", "reason": "Credential-free public QuickLink participant route used only by the hosted QuickLink page, not the SDKs.", @@ -939,7 +1023,7 @@ "method": "PATCH", "path": "/messaging/{session}/business/catalog/cart", "operationId": "setBusinessCartEnabled", - "fingerprint": "f7f94339bab5331a0323282442e040e46bf70aeaaf09ab6b3a887de3cdf2c2e0", + "fingerprint": "25ecb3cfb261e379fddc8357613e0ee4ee418ee34031ba984ec5922f4df70c4a", "typescript": { "status": "covered", "method": "MessagingClient.business.setCartEnabled" @@ -950,7 +1034,7 @@ "method": "PATCH", "path": "/messaging/{session}/business/collections/{collectionId}", "operationId": "updateBusinessCollection", - "fingerprint": "a7a9ea3f60c4700989177c97cb629788334199e945fda811b4a7e86e387e0692", + "fingerprint": "bce946f91506a156ad7fd377dc7d0a8ee99be6cdee90286f5a5f306353ef6069", "typescript": { "status": "covered", "method": "MessagingClient.business.updateCollection" @@ -961,7 +1045,7 @@ "method": "PATCH", "path": "/messaging/{session}/business/products/{productId}/visibility", "operationId": "setBusinessProductVisibility", - "fingerprint": "ce7c0a1832119bdd5f253f005b9a5acf4accf3c509670d318d62137d7f51350f", + "fingerprint": "8ad17a132ed325a6f3119ecf485dae03c54730fa62c5dc586a4f2ffbe544fa1d", "typescript": { "status": "covered", "method": "MessagingClient.business.setProductVisibility" @@ -972,7 +1056,7 @@ "method": "PATCH", "path": "/messaging/{session}/business/profile", "operationId": "updateBusinessProfile", - "fingerprint": "9ab8d9a6567afcd5d04a8394fd3368100e138a07e53e431ffe383648d8b95111", + "fingerprint": "2a84011f047c3a53014abd3e95bf5b0b8fbc8c7beb7e987c332dbed2b79a17c7", "typescript": { "status": "covered", "method": "MessagingClient.business.updateProfile" @@ -983,7 +1067,7 @@ "method": "PATCH", "path": "/messaging/projects/{projectSlug}/campaigns/{id}", "operationId": "updateProjectCampaign", - "fingerprint": "fe6366542a59aaf35a743b1d4fc2819fc0c192aeca7db53e41a6370de886f3fa", + "fingerprint": "508f4f2a061c0b698df2f91ee8cb6ba0696ab888aadfb91db00f85182559c933", "typescript": { "status": "covered", "method": "MessagingClient.campaigns.update" @@ -994,7 +1078,7 @@ "method": "PATCH", "path": "/messaging/projects/{projectSlug}/templates/{id}", "operationId": "updateProjectTemplate", - "fingerprint": "f32b61e565755f6c4c8e2abf76caee79466801323add172f5ba02d630e8b2603", + "fingerprint": "e22c08ca2f500336f8b2ef1e0de85c001592e5a056861823ac5c3624bddc5d8c", "typescript": { "status": "covered", "method": "MessagingClient.templates.update" @@ -1005,7 +1089,7 @@ "method": "PATCH", "path": "/quicklink/{token}/testing", "operationId": "customizeQuickLinkTesting", - "fingerprint": "9c07cbe18cd76e75c53b5ed4dc11bc1336b29c1e8b89bcd0895feb4a4a9ad0a3", + "fingerprint": "be396d7920cdcdc84fea9225fc5360a530f0ea21cabed92798b240182dae908f", "typescript": { "status": "excluded", "reason": "Hosted QuickLink recipient and authenticated Console-only operations are outside the trusted-server SDK surface." @@ -1016,7 +1100,7 @@ "method": "POST", "path": "/messaging/{session}/business/catalog", "operationId": "createBusinessCatalog", - "fingerprint": "088834ce309b050c5de43571db571deb3f601aa6e9fcbc944e007f390d751dce", + "fingerprint": "f6f6326d92e171b2ee563f4b372c7e32f8d93988f79253261a02db963d7875a6", "typescript": { "status": "covered", "method": "MessagingClient.business.createCatalog" @@ -1027,7 +1111,7 @@ "method": "POST", "path": "/messaging/{session}/business/collections", "operationId": "createBusinessCollection", - "fingerprint": "6566fce9d0136fa20ed2c506f3288d2d8736ee44baf4ca982a858590f4545862", + "fingerprint": "3c6011e8356d30bf5fff63d6e0650e586a56be8358892cd06b87c827de69c95b", "typescript": { "status": "covered", "method": "MessagingClient.business.createCollection" @@ -1038,7 +1122,7 @@ "method": "POST", "path": "/messaging/{session}/business/collections/{collectionId}/appeal", "operationId": "appealBusinessCollection", - "fingerprint": "f0f9d43290b94408fcdcc9a5134dce1af245e71efbf157cab31dc470c4cf5345", + "fingerprint": "d8709a8d3c97e88b7123565c704c2247e72e8b37b5dfa749dccd63f65e2fde28", "typescript": { "status": "covered", "method": "MessagingClient.business.appealCollection" @@ -1049,7 +1133,7 @@ "method": "POST", "path": "/messaging/{session}/business/collections/reorder", "operationId": "reorderBusinessCollections", - "fingerprint": "f2efe88f22d443cdf16e1e76c48752ab01b64e9381e8c7edadc83f4790b932b2", + "fingerprint": "caa64dbcd9c4f0d415286b658f7ec0804dc5939ddabb31f8fb2ea74b37c0b28d", "typescript": { "status": "covered", "method": "MessagingClient.business.reorderCollections" @@ -1060,7 +1144,7 @@ "method": "POST", "path": "/messaging/{session}/business/orders/{orderId}/lookup", "operationId": "getBusinessOrder", - "fingerprint": "1f7f11b1acbac7b43b8991b379526a2db723e4b0a3c0d5c0a02a08a1ec7e7964", + "fingerprint": "963e49033b8ceb96ee3a264c3a6acc924564bab828b26355680a9760608faa81", "typescript": { "status": "covered", "method": "MessagingClient.business.getOrder" @@ -1071,7 +1155,7 @@ "method": "POST", "path": "/messaging/{session}/business/products", "operationId": "createBusinessProduct", - "fingerprint": "e41fc9fe18d68eb7fa869c39346c557c852b66b9bde60604bcbcd6434adfb1cb", + "fingerprint": "32246cf4de7793a6bba84f25550f2d2b0cbb91381f47dd09a0ef16ae92fae371", "typescript": { "status": "covered", "method": "MessagingClient.business.createProduct" @@ -1082,7 +1166,7 @@ "method": "POST", "path": "/messaging/{session}/business/products/{productId}/appeal", "operationId": "appealBusinessProduct", - "fingerprint": "a6b97f4924e240033e024a750ad77f4c6449af74a34bbd28282bf65602a2bc20", + "fingerprint": "dae06a09934651623b3683f6afaf38a9fc75ef316d4f0e837cf33656ffbc5445", "typescript": { "status": "covered", "method": "MessagingClient.business.appealProduct" @@ -1093,7 +1177,7 @@ "method": "POST", "path": "/messaging/{session}/business/quick-replies", "operationId": "createBusinessQuickReply", - "fingerprint": "959776cbfe2e691d99444f71333fae3bf15d75d498e245972c3895f77f1beebc", + "fingerprint": "4b0b990c9839bed4f2c9a2163522bfafcb119089e0a8a63a555ac9b523732338", "typescript": { "status": "covered", "method": "MessagingClient.quickReplies.create" @@ -1104,7 +1188,7 @@ "method": "POST", "path": "/messaging/{session}/calls/{callId}/reject", "operationId": "rejectCall", - "fingerprint": "c21ae7e470d76f56c65947ad4bdfd4177d9f5abff4ee679a26bd2cf7efb6b95b", + "fingerprint": "d934f43fdc46c3271539123ca1a7841e896b83c7365782c21efb2d058f8ddd36", "typescript": { "status": "covered", "method": "MessagingClient.calls.reject" @@ -1115,7 +1199,7 @@ "method": "POST", "path": "/messaging/{session}/channels", "operationId": "createChannel", - "fingerprint": "bf2d992433f546d4f2256145bdc2d235c06b42d2932d7b8e30118db35aab4401", + "fingerprint": "b6e9051ae654dd336b86f132c6f9fb8d319ae923173e1149e5c019c3d9437b1a", "typescript": { "status": "covered", "method": "MessagingClient.channels.create" @@ -1126,7 +1210,7 @@ "method": "POST", "path": "/messaging/{session}/channels/{id}/follow", "operationId": "followChannel", - "fingerprint": "61ecc0d3f176000f10259d708a95a035b691a870fb6d0ed48d5c242c0637fbf4", + "fingerprint": "ba39536de33ca7d0589d0544260965c9a4155a5c40a83073518556d8eeac4bad", "typescript": { "status": "covered", "method": "MessagingClient.channels.follow" @@ -1137,7 +1221,7 @@ "method": "POST", "path": "/messaging/{session}/channels/{id}/live-updates", "operationId": "subscribeChannelLiveUpdates", - "fingerprint": "88d0f5b8f3fb51bc0bf2182aad6ff7efb439e6c771386fdc567333e742ad7507", + "fingerprint": "48ec990f6f5bbe111693eb5ceed904f8ce242f70e2d1dd683339e691aa0a0e44", "typescript": { "status": "covered", "method": "MessagingClient.channels.subscribeToLiveUpdates" @@ -1148,7 +1232,7 @@ "method": "POST", "path": "/messaging/{session}/channels/{id}/messages/{messageId}/reaction", "operationId": "reactToChannelMessage", - "fingerprint": "5071d9e728d5a6180aa869117a15635ec21b3d70bfeb35da92c595fd04989570", + "fingerprint": "75a23028cfb3423d2b5e6c1cc52b9bc7e1ca639c1581ec20294e0f58224055dd", "typescript": { "status": "covered", "method": "MessagingClient.channels.reactToMessage" @@ -1159,7 +1243,7 @@ "method": "POST", "path": "/messaging/{session}/channels/{id}/messages/{messageId}/viewed", "operationId": "markChannelMessageViewed", - "fingerprint": "4f10d434be12fe3f5e7c5122c771b5403770c8b8ba5dcca72a59b94c8fc9c775", + "fingerprint": "6833b5dcf7bbb04c651455915b4a15e605708a65fce85015e25b1ef0b8054642", "typescript": { "status": "covered", "method": "MessagingClient.channels.markMessageViewed" @@ -1170,7 +1254,7 @@ "method": "POST", "path": "/messaging/{session}/channels/{id}/mute", "operationId": "muteChannel", - "fingerprint": "c84e7240619e58893a1803625883cbd0ddeb87e287b89faafd3ec0a614da32af", + "fingerprint": "55494a4d257e6b9e9f6431b8b53cede0ad73bd569aded139e7d1babe7c5122a7", "typescript": { "status": "covered", "method": "MessagingClient.channels.mute" @@ -1181,7 +1265,7 @@ "method": "POST", "path": "/messaging/{session}/channels/{id}/unfollow", "operationId": "unfollowChannel", - "fingerprint": "ff93e18f76d9922c6a4b86f1848dfd7b51d5fe91aacc688a26671b13325dba6f", + "fingerprint": "28d9eda040473342604bf4676b03fa34bd2c305cd54a2cbf53659f9befee7615", "typescript": { "status": "covered", "method": "MessagingClient.channels.unfollow" @@ -1192,7 +1276,7 @@ "method": "POST", "path": "/messaging/{session}/channels/{id}/unmute", "operationId": "unmuteChannel", - "fingerprint": "e0b530177041e79f290550005cf7521cbb2b5b78c6209d3e3fd1534ffb9d69ba", + "fingerprint": "f017dd939c4c361be811d3aaad7803aff194ed376ab4659b6008272bdf66ad9b", "typescript": { "status": "covered", "method": "MessagingClient.channels.unmute" @@ -1203,7 +1287,7 @@ "method": "POST", "path": "/messaging/{session}/chats/{conversation}/archive", "operationId": "archiveChat", - "fingerprint": "17a641b261ab588793d255167acbeeb1710dbd84c99e7975f5d24ec16632a091", + "fingerprint": "8df22a3f2f8a424b8147c2a2d112469bed70fd2a13df2f1c6912b91f75c1c44c", "typescript": { "status": "covered", "method": "MessagingClient.chats.archive" @@ -1214,18 +1298,30 @@ "method": "POST", "path": "/messaging/{session}/chats/{conversation}/unarchive", "operationId": "unarchiveChat", - "fingerprint": "8acec6b0233c47893c31a324ab5be8cefcf7c57e939f16c6fa36d9a1318e34f8", + "fingerprint": "ae6272b8c64aebb0aaf35c2bc901c43272b0cb0d9ac801974f7424e187cef010", "typescript": { "status": "covered", "method": "MessagingClient.chats.unarchive" } }, + { + "family": "messaging", + "method": "POST", + "path": "/messaging/{session}/cloud-credentials/reauthorize", + "operationId": "reauthorizeCloudCredentials", + "fingerprint": "0e33bee1b6ad2ae718377dc12dc862b309ac48e303514fbe3b9b15005903f0dd", + "typescript": { + "status": "missing", + "reason": "No typed Official API credential reauthorization method in the SDK yet.", + "milestone": "official-api-consumers" + } + }, { "family": "messaging", "method": "POST", "path": "/messaging/{session}/contacts/{contactId}/block", "operationId": "blockContact", - "fingerprint": "9655fd43e2f1b3f4576168db1695cc13de7d4acd20658c1b5aac0e284fc996a7", + "fingerprint": "efaa09b3515a2f098ef7b92d2bc392bba6f5dfa36a163ffbffdb1348241d3ff4", "typescript": { "status": "covered", "method": "MessagingClient.contacts.block" @@ -1236,7 +1332,7 @@ "method": "POST", "path": "/messaging/{session}/contacts/{contactId}/unblock", "operationId": "unblockContact", - "fingerprint": "1d0693fe2753ebd2589ee8d17373ed06b9c9c9896a807574c407966965136308", + "fingerprint": "869cd02b4d16bb48a99588a0118c5eece5db88dc4bcb0e21913e2d07e8ad1a2d", "typescript": { "status": "covered", "method": "MessagingClient.contacts.unblock" @@ -1247,7 +1343,7 @@ "method": "POST", "path": "/messaging/{session}/groups", "operationId": "createGroup", - "fingerprint": "a9ca7842f2d3bcd46ca833ba0b77d131624e326ba0e5c66424468c7c19a3a051", + "fingerprint": "00f29a36474f7c73c2dee12496381eacee3db59e969b8a0d12387dfdd7ec31c2", "typescript": { "status": "covered", "method": "MessagingClient.groups.create" @@ -1258,7 +1354,7 @@ "method": "POST", "path": "/messaging/{session}/groups/{id}/admin/demote", "operationId": "demoteGroupAdmin", - "fingerprint": "d183bfae19ef39857f5d132c3b3e9ef8a15ef47de50ae64029461f41a6423624", + "fingerprint": "6e84fbf3cd309c78c13a558783460fca0abd1e5b1098a3867ee625e90086041b", "typescript": { "status": "covered", "method": "MessagingClient.groups.demoteParticipants" @@ -1269,7 +1365,7 @@ "method": "POST", "path": "/messaging/{session}/groups/{id}/admin/promote", "operationId": "promoteGroupAdmin", - "fingerprint": "36fa11e9425656bbe721d02bf46d931a763e119c00c05fe5c523056e73041d56", + "fingerprint": "4ddc4f4f79944666773ee5ad1bb518f4841ee75e606c8a460885aaceb5d3edb3", "typescript": { "status": "covered", "method": "MessagingClient.groups.promoteParticipants" @@ -1280,7 +1376,7 @@ "method": "POST", "path": "/messaging/{session}/groups/{id}/invite-code/revoke", "operationId": "revokeGroupInviteCode", - "fingerprint": "2e0933c7bcf8ec4b2820162b127f0f9a2029971af59c65e6abc092d2c00b5142", + "fingerprint": "0b2b3f90957ed9f4c67be752be6395fdb262ff847ae2d7bd9c87517e76d65ed5", "typescript": { "status": "covered", "method": "MessagingClient.groups.revokeInviteCode" @@ -1291,7 +1387,7 @@ "method": "POST", "path": "/messaging/{session}/groups/{id}/leave", "operationId": "leaveGroup", - "fingerprint": "f5634a097ef324614c7d503d9d0da851681ff3a767a8aea90fc6e52ac28b5b88", + "fingerprint": "7d097a88611fb1cd128e156346ac76a6d0822353faa05d43c471e8fa8831efd0", "typescript": { "status": "covered", "method": "MessagingClient.groups.leave" @@ -1302,7 +1398,7 @@ "method": "POST", "path": "/messaging/{session}/groups/{id}/participants/add", "operationId": "addGroupParticipants", - "fingerprint": "fbf5c232d456fa516ac90ede4804b9e4ba052de4036c283bd665282767311490", + "fingerprint": "2e37c97b53c92fa1d9065281338a7a0c17ec54898a1cb316e3ed91c3dc272b09", "typescript": { "status": "covered", "method": "MessagingClient.groups.addParticipants" @@ -1313,7 +1409,7 @@ "method": "POST", "path": "/messaging/{session}/groups/{id}/participants/remove", "operationId": "removeGroupParticipants", - "fingerprint": "0483bed4b31a574a8d8516a5f40162ba40390c9fe94d801f1a59ad5f5b50f7a8", + "fingerprint": "31cefb26ffaae8dabe0edf80144cb8a76e6a3d6dca7997594b647829e7330d17", "typescript": { "status": "covered", "method": "MessagingClient.groups.removeParticipants" @@ -1324,7 +1420,7 @@ "method": "POST", "path": "/messaging/{session}/groups/join", "operationId": "joinGroup", - "fingerprint": "505f491cd538f6ebc33e8102f235161664193255a10219376a91435ba6bc1f88", + "fingerprint": "e23cf4d394f9fbb920f2bc43c02edd8f33ad103442cf8e722c28193c32cefd01", "typescript": { "status": "covered", "method": "MessagingClient.groups.join" @@ -1335,7 +1431,7 @@ "method": "POST", "path": "/messaging/{session}/labels", "operationId": "createLabel", - "fingerprint": "142e907aff3544c82b091694402a545e941e56af7e8dd480e9a3f0216646232a", + "fingerprint": "d9fbb6cf7322e8b93a37265c36044499e642871c2c2afdbf08027475f3b721ed", "typescript": { "status": "covered", "method": "MessagingClient.labels.create" @@ -1346,7 +1442,7 @@ "method": "POST", "path": "/messaging/{session}/messages/react", "operationId": "sendReaction", - "fingerprint": "79deb5f04a3d6b0de7b5331f0148a96336ae44d829625f96516faade20407bcd", + "fingerprint": "bdd4a7caf0432c5c1189e5aa02dad4e5ff8a079ceab5b341a25eb4f70d545de2", "typescript": { "status": "covered", "method": "MessagingClient.messages.react" @@ -1357,7 +1453,7 @@ "method": "POST", "path": "/messaging/{session}/messages/seen", "operationId": "sendSeen", - "fingerprint": "fecffceb6a3f570c5777674c352d20ce5acb56e2f57fbd59fba705e261a4830a", + "fingerprint": "fe9dcd1f37d5b2d0f66b081b27b95515bc7d4400b3ce51153857f3ebf1746cb5", "typescript": { "status": "covered", "method": "MessagingClient.messages.markSeen" @@ -1368,7 +1464,7 @@ "method": "POST", "path": "/messaging/{session}/messages/send", "operationId": "sendMessage", - "fingerprint": "a231c6095b087ba06d5d5db608c25ad428d49ed412ebec25855c5d0e841fbe32", + "fingerprint": "3c49765cf915408e81088d1923604e1e49438f236fbe5cf39f378941dabf5dcf", "typescript": { "status": "covered", "method": "MessagingClient.messages.send" @@ -1379,7 +1475,7 @@ "method": "POST", "path": "/messaging/{session}/messages/star", "operationId": "starMessage", - "fingerprint": "d06112be793c2bc0cfcf3f45af1d4c1e5a8a392797ec36dedf66ac1028a37584", + "fingerprint": "ef2497fa9beac690cdc86d31947c21d91907fc3839b06573ab8612999cb69672", "typescript": { "status": "covered", "method": "MessagingClient.messages.star" @@ -1390,7 +1486,7 @@ "method": "POST", "path": "/messaging/{session}/messages/typing", "operationId": "setTyping", - "fingerprint": "8d5ddd310b46743def73f9f51d01053b0cc585fcf161a87ec1c5acba4b8fbe33", + "fingerprint": "ebda5ebce5bf898325a0dc6e68755a1cbb38cc06cc031db5e85fd53ca6844bb7", "typescript": { "status": "covered", "method": "MessagingClient.messages.setTyping" @@ -1401,7 +1497,7 @@ "method": "POST", "path": "/messaging/{session}/pair/code", "operationId": "requestPairCode", - "fingerprint": "548e42da84b680c442fa9f08038a0cbbdc4a7c5ef484254b21c025fce462e6b4", + "fingerprint": "ca8f4dfe9a3e388138a041ebf5c1a9150464210bc2a0f87b024f5590d0bf885e", "typescript": { "status": "covered", "method": "MessagingClient.sessions.requestPairingCode" @@ -1412,7 +1508,7 @@ "method": "POST", "path": "/messaging/{session}/presence", "operationId": "setPresence", - "fingerprint": "81b63d0f825dd57b40e5162ab0a8b2f78ce613aa11c43842f2bcb4a6c64d25da", + "fingerprint": "91c4602c57ee9077bf3541856103d3761e51ab2058554c40ec16dc2e048f76f0", "typescript": { "status": "covered", "method": "MessagingClient.presence.set" @@ -1423,18 +1519,30 @@ "method": "POST", "path": "/messaging/{session}/presence/{conversation}/subscribe", "operationId": "subscribePresence", - "fingerprint": "a833659c1e9f2f79414e846b7b2a01707800b24282379e5e371d5dddbd032002", + "fingerprint": "4b3e9a3396c4999cfdb956feb33bb67dbd0d2b6c637fe8a22f352ec56d4e9edb", "typescript": { "status": "covered", "method": "MessagingClient.presence.subscribe" } }, + { + "family": "messaging", + "method": "POST", + "path": "/messaging/{session}/templates", + "operationId": "createTemplate", + "fingerprint": "4d1d16a052d9b1d3dbb8ccde6abc853a98afb2c0ed4e1408be818af0c4cb0904", + "typescript": { + "status": "missing", + "reason": "No typed Cloud template method on this branch; MessagingClient.cloudTemplates.create is proposed in SDK PR #308.", + "milestone": "official-api-consumers" + } + }, { "family": "messaging", "method": "POST", "path": "/messaging/cloud-api/embedded-signup", "operationId": "embeddedSignup", - "fingerprint": "ca09251cb32425a1dc7368c7c5c6af630ebd41dcec25e01717b89cd48cfabc0b", + "fingerprint": "149649df30d870fd5ce55803af9ad919274ebe674aafd2d2a086c7314a90df99", "typescript": { "status": "covered", "method": "MessagingClient.cloudOnboarding.advance" @@ -1445,7 +1553,7 @@ "method": "POST", "path": "/messaging/features/evaluate", "operationId": "evaluateFeatures", - "fingerprint": "4fcfc98dd98689726893fbb4e3afbb625a1abc1e0816ed44cdc063434db8f389", + "fingerprint": "014f7057b6d86f87fe43ee33273eda4ab966af2fe14f14c4e7b235d1ef3822cd", "typescript": { "status": "excluded", "reason": "Host-runtime feature evaluation is not a customer messaging operation in this SDK." @@ -1456,7 +1564,7 @@ "method": "POST", "path": "/messaging/media/{id}/download-and-save", "operationId": "downloadAndSaveMedia", - "fingerprint": "e3a5e9517ce5d654de83ec103972697c1a097a08c90b552738a188cd5a98e7c3", + "fingerprint": "9492f0ccb3df8d809324203d30a9ebcd9abe58cc52ea841331ad4fe762a81d31", "typescript": { "status": "covered", "method": "MessagingClient.media.persist" @@ -1467,7 +1575,7 @@ "method": "POST", "path": "/messaging/projects/{projectSlug}/campaigns", "operationId": "createCampaign", - "fingerprint": "4cedd525607544d02d4b31481358510b2ecb8b9c54cc6b0f749799e1011ff3f0", + "fingerprint": "c858b4f27fe9a56f8a6e89f40ea5f1e63806768c27f7f80e085f383afd932120", "typescript": { "status": "covered", "method": "MessagingClient.campaigns.create" @@ -1478,7 +1586,7 @@ "method": "POST", "path": "/messaging/projects/{projectSlug}/campaigns/{id}/launch", "operationId": "launchCampaign", - "fingerprint": "4af01830d796ffcfe37f6397cccc188c4546caf643b586a9925f137df9c7b4c2", + "fingerprint": "8bc723d1c383f16ef833d08c8cc0f8ccb5431c3e0f83683d17e12133a74b1126", "typescript": { "status": "covered", "method": "MessagingClient.campaigns.launch" @@ -1489,7 +1597,7 @@ "method": "POST", "path": "/messaging/projects/{projectSlug}/campaigns/{id}/pause", "operationId": "pauseCampaign", - "fingerprint": "cd056dc9925cf81e94b47a9a345e08287dd4d765ce9beb31ba13d78de5fd51ef", + "fingerprint": "6769cb5043ca55004973cb329193308155afc2026d076ffc21eea4afd199850b", "typescript": { "status": "covered", "method": "MessagingClient.campaigns.pause" @@ -1500,7 +1608,7 @@ "method": "POST", "path": "/messaging/projects/{projectSlug}/campaigns/{id}/recipients", "operationId": "addProjectCampaignRecipients", - "fingerprint": "943eefb8894a64fe72b01c6d16a7d8721e1ad1281a936acb6082c3cbd2fec855", + "fingerprint": "a7f90ae6a516661850333c9049384a7a6ccb42df088d18a882b2e7895bf3598c", "typescript": { "status": "covered", "method": "MessagingClient.campaigns.addRecipients" @@ -1511,7 +1619,7 @@ "method": "POST", "path": "/messaging/projects/{projectSlug}/campaigns/{id}/requeue", "operationId": "requeueCampaign", - "fingerprint": "0ed764141ea7484211a62c62c4496c37572548109cc0a5355c94cb02661c4f7d", + "fingerprint": "de9b406b914d015b8d99e6d2f14f1eeba2ab279ec09fc7dc156cfe9c1153c7a0", "typescript": { "status": "covered", "method": "MessagingClient.campaigns.requeue" @@ -1522,7 +1630,7 @@ "method": "POST", "path": "/messaging/projects/{projectSlug}/campaigns/{id}/reschedule", "operationId": "rescheduleCampaign", - "fingerprint": "b6c239ea6b3e2dc5df21cccdde97afb239664089f6654170ae3db917d94e6676", + "fingerprint": "35bd17781eeca00ab6e0fe132e25f1a4bf4d0065d08932755ba9dffff068ab29", "typescript": { "status": "covered", "method": "MessagingClient.campaigns.reschedule" @@ -1533,7 +1641,7 @@ "method": "POST", "path": "/messaging/projects/{projectSlug}/campaigns/{id}/resume", "operationId": "resumeCampaign", - "fingerprint": "9c0636666ce2e8678efc4a33a9b2fdac42435008f9a12486b57253c11df0764e", + "fingerprint": "e45e8e7dd2b7eacca457da9e90567054e740491e5c12c401c4e9ec4d207d3ea3", "typescript": { "status": "covered", "method": "MessagingClient.campaigns.resume" @@ -1544,7 +1652,7 @@ "method": "POST", "path": "/messaging/projects/{projectSlug}/campaigns/{id}/stop", "operationId": "stopCampaign", - "fingerprint": "6a09b0d2a67631403abfcd8b4eac52c29b25cfeb873f91d3a7f82236e8ced7d1", + "fingerprint": "7459ed402f10e6e7170f4dea98b37904c392d3fc1487a855185164d4070e8272", "typescript": { "status": "covered", "method": "MessagingClient.campaigns.stop" @@ -1555,7 +1663,7 @@ "method": "POST", "path": "/messaging/projects/{projectSlug}/templates", "operationId": "createProjectTemplate", - "fingerprint": "7a023b3c396651a3dfa6b6bb09a5fa458127bc0ce719b3af7a1953dccf232026", + "fingerprint": "cf764410d1d3385427e82e3e79aee06b60947a9ea0aa970237f363d44b915b52", "typescript": { "status": "covered", "method": "MessagingClient.templates.create" @@ -1566,7 +1674,7 @@ "method": "POST", "path": "/messaging/projects/{projectSlug}/templates/{id}/preview", "operationId": "previewProjectTemplate", - "fingerprint": "c8681de0b498d25368e0d3a101af25a7675c599a6d25c505cc2df74123755433", + "fingerprint": "064fa931dad5f4cf8b35efda74ec7970f21b01984e4e7090ef3d3a3797f0daad", "typescript": { "status": "covered", "method": "MessagingClient.templates.preview" @@ -1577,7 +1685,7 @@ "method": "POST", "path": "/messaging/projects/{projectSlug}/templates/{id}/submit", "operationId": "submitProjectTemplateToMeta", - "fingerprint": "22b195a6b593bb675f6b52477feab92a60bd41d7069ddc7b43229889ba35dd57", + "fingerprint": "f4af4c7344c18d8975f4972a47f5acfc0f988b9a0a93602bfe4ff13aebceef4f", "typescript": { "status": "covered", "method": "MessagingClient.templates.submit" @@ -1588,7 +1696,7 @@ "method": "POST", "path": "/messaging/quicklinks", "operationId": "createQuickLink", - "fingerprint": "ea9ebff159f89abaa65fe3e03549b43c95b3d9521d95f651e1307561030595ca", + "fingerprint": "162b239dd0e79bd2d411040bc86e22b2d78e68983f5f5c83108ccf8c1b1debcc", "typescript": { "status": "covered", "method": "MessagingClient.quickLinks.create" @@ -1599,7 +1707,7 @@ "method": "POST", "path": "/messaging/testing/{projectId}/events", "operationId": "triggerTestEvent", - "fingerprint": "b5257525dd5d250dd5df292894bf43b30c663c55a85c4c664033611e6f959118", + "fingerprint": "8625deeb37b2bddfc9c6acfecf793cbb5544cf72a7af3944c4e29e4284c513d4", "typescript": { "status": "covered", "method": "MessagingClient.testing.triggerEvent" @@ -1610,21 +1718,47 @@ "method": "POST", "path": "/messaging/testing/{projectId}/history-fixtures", "operationId": "createTestingHistoryFixture", - "fingerprint": "cc82bc67e848516a5a8169c3ab66a0d66b91d7885003434ba140bdaa8d02a7cd", + "fingerprint": "b11d0a08453525d1c204ebf73071fd0ee87e45ea3016e0b220d7c3d711b09517", "typescript": { "status": "covered", "method": "MessagingClient.testing.createHistoryFixture" } }, + { + "family": "messaging", + "method": "POST", + "path": "/messaging/voip/call-links", + "operationId": "voipCreateCallLink", + "fingerprint": "bf9fad45924d6db6e8ecc08b324c0f980fe86076784e6079e4ebc3c79905f6e7", + "typescript": { + "status": "missing", + "reason": "MessagingClient.voip.createCallLink: Implemented on SDK dev; not on this main-based branch until dev merges.", + "milestone": "sdk-dev-calls" + } + }, + { + "family": "messaging", + "method": "POST", + "path": "/messaging/voip/call-links/preview", + "operationId": "voipPreviewCallLink", + "fingerprint": "a0575bfda3c92adf4ed6a9c58e69ba44cd9fff3552c7874e9f3e0a1e91453fcf", + "typescript": { + "status": "missing", + "reason": "MessagingClient.voip.previewCallLink: Implemented on SDK dev; not on this main-based branch until dev merges.", + "milestone": "sdk-dev-calls" + } + }, { "family": "messaging", "method": "POST", "path": "/messaging/voip/calls", "operationId": "voipPlaceCall", - "fingerprint": "5e83b2f8992788ac0f500b69782b47a5063f24cf2bbe103451b3c696d262cdf9", + "fingerprint": "bf424563aacecdc394c67ae2bf882b2cfded4c66c3d60f5d8546a47ac2a15fbe", "typescript": { - "status": "covered", - "method": "MessagingClient.voip.place" + "status": "partial", + "method": "MessagingClient.voip.place", + "reason": "Single-recipient placement with `to` is typed. Group placement with `participants` or `groupId` is implemented on SDK dev, not on this branch.", + "milestone": "sdk-dev-calls" } }, { @@ -1632,7 +1766,7 @@ "method": "POST", "path": "/messaging/voip/calls/{id}/accept", "operationId": "voipAcceptCall", - "fingerprint": "86db3378c2d5fb58b7e2494c6ae2d00762d73d8817126fadc0f77f9b342fed73", + "fingerprint": "3c02a376f46e2caad3d5ac93984ce1ae5c57a65c949ed8f241470fa846eaceea", "typescript": { "status": "covered", "method": "MessagingClient.voip.accept" @@ -1643,18 +1777,30 @@ "method": "POST", "path": "/messaging/voip/calls/{id}/candidate", "operationId": "voipCandidate", - "fingerprint": "0d2c93046c09e15213b7b42a2c87d512034ad536866cfab97049e0cfb3a25b15", + "fingerprint": "fc2181fec07686e0324552e33468eda58490fe44a91bdfccaeb47f846ff20b89", "typescript": { "status": "excluded", "reason": "used internally by the Calls SDK; not part of the public API" } }, + { + "family": "messaging", + "method": "POST", + "path": "/messaging/voip/calls/{id}/hand", + "operationId": "setCallHandRaised", + "fingerprint": "65c68e95b86c19c75e9f7c7c8de6b4f58cc1ef537711a7570a48a37e9e7b6ca8", + "typescript": { + "status": "missing", + "reason": "MessagingClient.voip.setHandRaised: Implemented on SDK dev; not on this main-based branch until dev merges.", + "milestone": "sdk-dev-calls" + } + }, { "family": "messaging", "method": "POST", "path": "/messaging/voip/calls/{id}/leave", "operationId": "voipLeaveCall", - "fingerprint": "f7c2aec2289eec2eb3f0429f3f2ae75c4bf86c31de54600403395677e6d05aa9", + "fingerprint": "2c8fb3e1ff5cc305096e5fbfb315f61f5815998dcd61dfa45c7520b7fc58eb9f", "typescript": { "status": "covered", "method": "MessagingClient.voip.leave" @@ -1665,7 +1811,7 @@ "method": "POST", "path": "/messaging/voip/calls/{id}/offer", "operationId": "voipOffer", - "fingerprint": "d32154df56b41060a43b933d473ef211fad6d9cceae7a9abd5fd83781cf5c7b0", + "fingerprint": "c5807b27029f96581185b7448d2ea1859825f349c4a9fba25cce1f48b74af7ca", "typescript": { "status": "excluded", "reason": "used internally by the Calls SDK; not part of the public API" @@ -1676,18 +1822,42 @@ "method": "POST", "path": "/messaging/voip/calls/{id}/participants", "operationId": "voipAddParticipant", - "fingerprint": "1c2964f6b2f7f1922bb5baad49363fbcdba2e0d805546b9923cdf9e05483c243", + "fingerprint": "4b66d36e07be198172c22b39461348bf8a79f9fe44080e282f7c4105762113e6", "typescript": { "status": "covered", "method": "MessagingClient.voip.addParticipant" } }, + { + "family": "messaging", + "method": "POST", + "path": "/messaging/voip/calls/{id}/participants/ring", + "operationId": "voipRingParticipant", + "fingerprint": "1b273e5ddcdb6e2d57d3abcc2f05a01a51f93701f03579b9eec5928500e0a394", + "typescript": { + "status": "missing", + "reason": "MessagingClient.voip.ringParticipant: Implemented on SDK dev; not on this main-based branch until dev merges.", + "milestone": "sdk-dev-calls" + } + }, + { + "family": "messaging", + "method": "POST", + "path": "/messaging/voip/calls/{id}/reaction", + "operationId": "sendCallReaction", + "fingerprint": "4ba89adf4ee046bb37aff1c45dcb0762fb47957ebb7fa15196325b241ff27b0b", + "typescript": { + "status": "missing", + "reason": "MessagingClient.voip.sendReaction: Implemented on SDK dev; not on this main-based branch until dev merges.", + "milestone": "sdk-dev-calls" + } + }, { "family": "messaging", "method": "POST", "path": "/messaging/voip/calls/{id}/reject", "operationId": "voipRejectCall", - "fingerprint": "aac9ebc9522a756f68841ae99e9daa5e157f1af5e7151474401acfaa76da1aa1", + "fingerprint": "fab5e62389e555712ec74641cdff84254e702a9fd3ebb4970cb522b13021f920", "typescript": { "status": "covered", "method": "MessagingClient.voip.reject" @@ -1698,7 +1868,7 @@ "method": "POST", "path": "/messaging/voip/calls/{id}/renegotiate", "operationId": "voipRenegotiate", - "fingerprint": "484038a1951bd5aafa95a7e96186d6934d3e8fcb980448ba67aa5a8563ae4016", + "fingerprint": "02b7a908bfcc8fb722ebab089ca3b3c796846351659b7c15652794c92852396e", "typescript": { "status": "excluded", "reason": "used internally by the Calls SDK; not part of the public API" @@ -1709,7 +1879,7 @@ "method": "POST", "path": "/messaging/voip/calls/{id}/reports", "operationId": "voipReportCallDiagnostics", - "fingerprint": "12d0fd1b28b4ba0ab61e9645308e3e898ccfa5384f2b195c0330d03cd618d0c7", + "fingerprint": "90442f3c5d8e6e1fbc81609f0e5b41496f0c5b21bd449ff0f0e879dbe5ad6661", "typescript": { "status": "covered", "method": "MessagingClient.voip.report" @@ -1720,7 +1890,7 @@ "method": "POST", "path": "/messaging/voip/calls/check", "operationId": "checkCall", - "fingerprint": "33adf8f2983a23af927b99e7b427aad6780668daa6195e78b0219117e592d64a", + "fingerprint": "a202f2a888cb9beda4be76b53d2bc4a7f642bdea25146b6e5c3bf5c24f5923fb", "typescript": { "status": "covered", "method": "MessagingClient.voip.check" @@ -1731,7 +1901,7 @@ "method": "POST", "path": "/messaging/webhooks", "operationId": "createWebhook", - "fingerprint": "1ebca9abc706acd43d17d62c4365b340457bea04e903fbbe06bcacedab1266d6", + "fingerprint": "2a3c161027d0b3fa32b80f40009aff2b11315da620c9ebeee014c524bdbd89b5", "typescript": { "status": "covered", "method": "MessagingClient.webhooks.create" @@ -1800,7 +1970,7 @@ "method": "POST", "path": "/quicklink/{token}/history", "operationId": "chooseQuickLinkHistory", - "fingerprint": "432431a646f8bead00c097a9600d3435728a5114db82a76d0ff595098f1921d9", + "fingerprint": "dad07112cc92c58a1d40e633e18a4063ce5efe6d96627aff79d998ba0ada0d8c", "typescript": { "status": "excluded", "reason": "Hosted QuickLink recipient and authenticated Console-only operations are outside the trusted-server SDK surface." @@ -1833,7 +2003,7 @@ "method": "POST", "path": "/quicklink/{token}/testing/start", "operationId": "startQuickLinkTesting", - "fingerprint": "0a49cae94073bb15d821218ccfd02c81f74e1d88ea48522ce6526bf06bf2d220", + "fingerprint": "a2ecca9020669b79bda1426f1a239eb30e5ef1e7b313d00da4f5eaacb7cd7678", "typescript": { "status": "excluded", "reason": "Hosted QuickLink recipient and authenticated Console-only operations are outside the trusted-server SDK surface." @@ -1844,7 +2014,7 @@ "method": "PUT", "path": "/messaging/{session}/business/compliance", "operationId": "setBusinessMerchantCompliance", - "fingerprint": "fc52465c0481189a44411c710cac8a7aaf8ea2812770165140dff3404724c79f", + "fingerprint": "80597573ecf95fd61fb73115182b8da8cb164023e1a6181b9337c62397b7f2d6", "typescript": { "status": "covered", "method": "MessagingClient.business.setMerchantCompliance" @@ -1855,7 +2025,7 @@ "method": "PUT", "path": "/messaging/{session}/business/products/{productId}", "operationId": "updateBusinessProduct", - "fingerprint": "2c0b47a2e44111dc69f96cb26974d2a52c055950e9a5f5ec2c1b28f4364a5dd0", + "fingerprint": "bf960e9636001cdb53f31c585d11c4a33a82dda485a5f40848fbcab9d7552aa7", "typescript": { "status": "covered", "method": "MessagingClient.business.updateProduct" @@ -1866,7 +2036,7 @@ "method": "PUT", "path": "/messaging/{session}/business/profile/cover-photo", "operationId": "setBusinessCoverPhoto", - "fingerprint": "6dd89e9ae297cf36e6a8e381752297a22175793e12434579001316602237c497", + "fingerprint": "17bdfe12eafde65efd4db0d9873fa7fd85da90f33e9764f786ad9cff347648ac", "typescript": { "status": "covered", "method": "MessagingClient.business.setCoverPhoto" @@ -1877,7 +2047,7 @@ "method": "PUT", "path": "/messaging/{session}/business/quick-replies/{quickReplyId}", "operationId": "setBusinessQuickReply", - "fingerprint": "46d9bc72756dcc534bcfc947009a873e4aa24cbbde49885fb9b52392d2aba643", + "fingerprint": "d19737adfcd109378a001d9d7be61b3a5652fd6476999bf50c3fdc49f9073770", "typescript": { "status": "covered", "method": "MessagingClient.quickReplies.replace" @@ -1888,7 +2058,7 @@ "method": "PUT", "path": "/messaging/{session}/chats/{conversation}/disappearing", "operationId": "setChatDisappearingTimer", - "fingerprint": "5b401e12339f853a42040d73a93504705d1cdcebee91a5ecc775c2ceec8cd860", + "fingerprint": "d0622b69dadabde27256386e36402a48ef3de3cd90812a95f4883fd21b0ae464", "typescript": { "status": "covered", "method": "MessagingClient.chats.setDisappearingTimer" @@ -1899,7 +2069,7 @@ "method": "PUT", "path": "/messaging/{session}/chats/{conversation}/messages/{messageId}", "operationId": "editMessage", - "fingerprint": "a7a0c3e45a94bb77a8e6ea2fae1bf343cfd8972e65dff0cb1eab91a7b7e6ce2c", + "fingerprint": "983c66d6285c3e1d24a2bd61435aa350a3da75c0abe22cb3c0d57d0ad675c9fe", "typescript": { "status": "covered", "method": "MessagingClient.chats.editMessage" @@ -1910,7 +2080,7 @@ "method": "PUT", "path": "/messaging/{session}/groups/{id}/description", "operationId": "setGroupDescription", - "fingerprint": "33dc8f018ee4337dfd778849d0be81eb6582774b9c8449818ae58cc54760bf16", + "fingerprint": "d92146bfb3fc429721441909789bf2dda7a6688356973b1737068ed60fc7ebec", "typescript": { "status": "covered", "method": "MessagingClient.groups.setDescription" @@ -1921,7 +2091,7 @@ "method": "PUT", "path": "/messaging/{session}/groups/{id}/picture", "operationId": "setGroupPicture", - "fingerprint": "8c58d71c69cf249d2c4311a0a60e445222fad6f0e5149e41fccb9d7a158cfc1c", + "fingerprint": "a1de1f71b01c5316b8355e8640aff26374a25ad588865cfec2482f3cd7c5dc02", "typescript": { "status": "covered", "method": "MessagingClient.groups.setPicture" @@ -1932,7 +2102,7 @@ "method": "PUT", "path": "/messaging/{session}/groups/{id}/settings/info-edit", "operationId": "setGroupInfoEditing", - "fingerprint": "185bca6336adbb00a704fd52676b0ebf4f5284c9f3e2d4561f17935f33796f37", + "fingerprint": "5d55875b7023ac9fde1644b7a5a4fcff5b2bd9103b1d6195265232f3d2dd5dc7", "typescript": { "status": "covered", "method": "MessagingClient.groups.setInfoEditing" @@ -1943,7 +2113,7 @@ "method": "PUT", "path": "/messaging/{session}/groups/{id}/settings/join-approval", "operationId": "setGroupJoinApproval", - "fingerprint": "de669123d6e9437c876946bccc1f1581c996717e6397533ff02ec0e705e7a8fa", + "fingerprint": "60273127b8bc1ddf70ecb59202f8df9048d437eb1fb2ea2445f98c52fa8a30f0", "typescript": { "status": "covered", "method": "MessagingClient.groups.setJoinApproval" @@ -1954,7 +2124,7 @@ "method": "PUT", "path": "/messaging/{session}/groups/{id}/settings/member-add", "operationId": "setGroupMemberAddMode", - "fingerprint": "fc6eda81991c5374bf357a20fe0155a5af5e7217b88d05e6d17e8924ab772ea8", + "fingerprint": "5281b29be47d7c2f54c507717f06215573d5702e83b69c8c9d00b277929b0844", "typescript": { "status": "covered", "method": "MessagingClient.groups.setMemberAddMode" @@ -1965,7 +2135,7 @@ "method": "PUT", "path": "/messaging/{session}/groups/{id}/settings/messages", "operationId": "setGroupMessaging", - "fingerprint": "04f34bdc92f41e31a4677f8b51233777b2fb6def14bdc2c653b1b6474108180b", + "fingerprint": "be8350d7177fc80ced8946e4e58c54bc61e13a1e85a1f63fc9f31496fdcbd0ed", "typescript": { "status": "covered", "method": "MessagingClient.groups.setMessaging" @@ -1976,7 +2146,7 @@ "method": "PUT", "path": "/messaging/{session}/groups/{id}/subject", "operationId": "setGroupSubject", - "fingerprint": "3cd0941470ffd1035a050df59517f0a4ac943d58d6b4550f3e54f9ff6040c619", + "fingerprint": "77d6bdc2647b366cafa8a662c32afcfb90973309b2acaaa8330a2c27dd5ddc86", "typescript": { "status": "covered", "method": "MessagingClient.groups.setSubject" @@ -1987,7 +2157,7 @@ "method": "PUT", "path": "/messaging/{session}/hybrid-link", "operationId": "setHybridLinkPaused", - "fingerprint": "6281b7e34d8c0ee20e785b04cc380a918ac976a68fe1850c9f6ba7fec06991b7", + "fingerprint": "e447543b29b41a91b90cc34d0df73d8ec51a98014c0583ab742a405f526a0db7", "typescript": { "status": "covered", "method": "MessagingClient.hybridLink.setPaused" @@ -1998,7 +2168,7 @@ "method": "PUT", "path": "/messaging/{session}/labels/{labelId}", "operationId": "updateLabel", - "fingerprint": "a6fdea356455eff684918cf0dfaca2af8743122c8715e1248f7ad37fac95f27e", + "fingerprint": "9641eaa7f25124e00a94b8c45f365aafed0ed38bef17f86fde80470ce5716552", "typescript": { "status": "covered", "method": "MessagingClient.labels.update" @@ -2009,7 +2179,7 @@ "method": "PUT", "path": "/messaging/{session}/labels/chats/{conversation}", "operationId": "setChatLabels", - "fingerprint": "41a067475d83bed90abe1b26a789673d3d006f933680bf12999b3752196c329d", + "fingerprint": "648a47e94b863fd0d55f571800c880eb7aa93f65fac3a9d9c7145b6b132d9356", "typescript": { "status": "covered", "method": "MessagingClient.labels.replaceForChat" @@ -2020,7 +2190,7 @@ "method": "PUT", "path": "/messaging/{session}/privacy/{setting}", "operationId": "setPrivacySetting", - "fingerprint": "30625dcfeb33711221e8a78813da1eca499f3743246696b074cd0bc9bee8f448", + "fingerprint": "2481980186a0246f4003477f31dc0f71ccc511a0bedb01efa2ed928b71d9281a", "typescript": { "status": "covered", "method": "MessagingClient.privacy.set" @@ -2031,7 +2201,7 @@ "method": "PUT", "path": "/messaging/{session}/privacy/disappearing/default", "operationId": "setDefaultDisappearingTimer", - "fingerprint": "e4f2dacf3600aa8a27196ad945cce5f34e938f5ee30f270666b3f7343fac3eac", + "fingerprint": "a83a5a63b354348cc029a3838ae3af0078779d0cfe549d140ff199e6fb865635", "typescript": { "status": "covered", "method": "MessagingClient.privacy.setDefaultDisappearingTimer" @@ -2042,7 +2212,7 @@ "method": "PUT", "path": "/messaging/{session}/profile/name", "operationId": "setProfileName", - "fingerprint": "0d065c4792104b6f964fab2ca4dd758240f9324abc58a2534207203f96e0681e", + "fingerprint": "fbc34b4437fba785c7d66a667bee52a37a9b5e3f32308690fe6e67443c8feb5c", "typescript": { "status": "covered", "method": "MessagingClient.profile.setName" @@ -2053,7 +2223,7 @@ "method": "PUT", "path": "/messaging/{session}/profile/picture", "operationId": "setProfilePicture", - "fingerprint": "e7e9bcbde6e6fc829d5f4bbe6bd180c5e84b007b7307c10a79ae7c5d849abf87", + "fingerprint": "eb0473bd8aa5f336af7e65bfebe4e1287829d0ac4a523c0f362b77766554e296", "typescript": { "status": "covered", "method": "MessagingClient.profile.setPicture" @@ -2064,7 +2234,7 @@ "method": "PUT", "path": "/messaging/{session}/profile/status", "operationId": "setProfileStatus", - "fingerprint": "371c99863bf82cc1cc82f21ab02b1a37e666283058ebaaafb3f19e149e7fe658", + "fingerprint": "ba3bbf75b8f7305b6b14de14007d3e730380e4cd47c4e9e5eed5c2fde7d8a345", "typescript": { "status": "covered", "method": "MessagingClient.profile.setStatus" @@ -2075,7 +2245,7 @@ "method": "PUT", "path": "/messaging/{session}/safe-mode", "operationId": "updateSessionSafeMode", - "fingerprint": "6ccf24b2d751b3c08da762dfd662c9f033710110537ddb8462d137a7a9bb9f53", + "fingerprint": "fec75aecaa5a202663938abc7b27c0f33bb19e323bd9673126340ee29122c4ca", "typescript": { "status": "covered", "method": "MessagingClient.banSafe.updateSessionSafeMode" @@ -2086,7 +2256,7 @@ "method": "PUT", "path": "/messaging/projects/{projectId}/health-policy", "operationId": "updateProjectHealthPolicy", - "fingerprint": "48aea0f68aa03ac48436cab07ffa64bdfa0dfe2fee8444e8af8d948dabec0bf3", + "fingerprint": "a874863848d1f2a7e1a30c0bf1e35cf95b5a4abaa8240206e6b31521540066c8", "typescript": { "status": "covered", "method": "MessagingClient.banSafe.updateProjectHealthPolicy" @@ -2097,7 +2267,7 @@ "method": "PUT", "path": "/messaging/projects/{projectId}/insurance-evidence", "operationId": "updateProjectInsuranceEvidence", - "fingerprint": "8941aa7b26599f010cca39770b7bf68e9a7040ec005909cc62181f1e74e056d0", + "fingerprint": "e01d50400fcf993ae73f2a87b267c4312428e522fbe9bcc2a6edb4f5c347cbd3", "typescript": { "status": "covered", "method": "MessagingClient.banSafe.updateProjectInsuranceEvidence" @@ -2108,7 +2278,7 @@ "method": "PUT", "path": "/messaging/projects/{projectId}/safe-mode", "operationId": "updateProjectSafeMode", - "fingerprint": "5faf7acf5065c3de739be5bb8e7746f232627af8ecd836d55bc3e03a0a07ef26", + "fingerprint": "61f5125360317e5ec72bc7cf7b9ca913e6f9515291420fb39494021727aac4b5", "typescript": { "status": "covered", "method": "MessagingClient.banSafe.updateProjectSafeMode" @@ -2119,7 +2289,7 @@ "method": "PUT", "path": "/messaging/projects/{projectId}/warmup-plan", "operationId": "updateProjectWarmupPlan", - "fingerprint": "2b9c4f34bd2ea503f953e3a6411a46a0f21ef40e684e2bd1ec9c7dd948ebb9a5", + "fingerprint": "7b4106d071b5daee92079be375c2f78a4d0470a201fe9d53cde64dc9937ed24f", "typescript": { "status": "covered", "method": "MessagingClient.banSafe.updateProjectWarmupPlan" @@ -2130,7 +2300,7 @@ "method": "PUT", "path": "/messaging/routing/hybrid", "operationId": "setHybridRoutingPolicy", - "fingerprint": "87335686e11577e2c6c68f4dc57051756af08cbbafd95b21355979af86763c54", + "fingerprint": "9cdde9940a853b7d182d4213eea007569d70ab1104c6e98606296a5d5fa8d8ca", "typescript": { "status": "covered", "method": "MessagingClient.hybridLink.setPolicy" @@ -2141,7 +2311,7 @@ "method": "PUT", "path": "/messaging/webhooks/{id}", "operationId": "updateWebhook", - "fingerprint": "0a1a8b6de76e6ab824e9f60bf179a2403348a69c24e9ae564829187dfc325694", + "fingerprint": "3ed72e7d9a289035bb3cd7ce15776009de36e1d477d54650077bbee10e5ca62e", "typescript": { "status": "covered", "method": "MessagingClient.webhooks.update" @@ -2317,7 +2487,7 @@ "method": "DELETE", "path": "/platform/call-opt-outs/{optOutId}", "operationId": "deleteCallOptOut", - "fingerprint": "cf2c14da2016dd90327b480fcf758ba3ac733ec9c78bb423d885b7f9376841fb", + "fingerprint": "3aaf97627e42784abc758c36cb52984b74edeb92d0ce568081854e973dbb19a6", "typescript": { "status": "covered", "method": "Client.callOptOuts.delete" @@ -2428,7 +2598,7 @@ "method": "DELETE", "path": "/platform/sessions/{session}/client-rules", "operationId": "deleteClientRules", - "fingerprint": "0244a31a6978d23f8bde1f96ec97693c51fcfba857a6907dc0a705cca07c3899", + "fingerprint": "7ed7280c6d5893f65e0736112b2f909902d7c8c1958c0bb67749d6075645f21e", "typescript": { "status": "covered", "method": "MessagingClient.clientTokens.deleteRules" @@ -2450,7 +2620,7 @@ "method": "DELETE", "path": "/platform/sip-trunks/{trunkId}", "operationId": "deleteSipTrunk", - "fingerprint": "5ec2c1ad81eed4e835b1b9ad6288d53490a8747883d1e13e03217814cec85b1e", + "fingerprint": "98965866b349f6576c3b6874ead849e42b020cc33bf7badec00c36981c624123", "typescript": { "status": "covered", "method": "Client.sipTrunks.delete" @@ -2473,7 +2643,7 @@ "method": "DELETE", "path": "/platform/voice/audio/{assetId}", "operationId": "deleteVoiceAudio", - "fingerprint": "628b9421a3c1e60c13f9f96d7cdef30d2a85db84451ca27b8b2d11897f4b75f3", + "fingerprint": "bf43dcb9ca7d36e9ca56149f80f266525e737d995ca7276b5b9859ddd84e50a5", "typescript": { "status": "covered", "method": "Client.voice.audio.delete" @@ -2484,7 +2654,7 @@ "method": "DELETE", "path": "/platform/voice/provider-credentials/{credentialId}", "operationId": "deleteVoiceProviderCredential", - "fingerprint": "7f8da96bfd047ec605a33e1c74735b24765498e5d6e437f0c7bbea92b4068989", + "fingerprint": "26be5ad8774f5101ae33a3c6f414c0a80772d2151a040643bf5e9888b0b0d347", "typescript": { "status": "covered", "method": "Client.voice.providerCredentials.delete" @@ -2501,6 +2671,30 @@ "method": "Client.webhooks.delete" } }, + { + "family": "platform", + "method": "GET", + "path": "/api/account/cloud-health", + "operationId": "getAccountCloudHealth", + "fingerprint": "b1e9cb588150cd3af46ed7bdfb4e2385e7e7da00b922c4155c53d854cfef201c", + "typescript": { + "status": "excluded", + "reason": "Console-only operation requires dashboard identity; not available to SDK credentials.", + "milestone": "credential-boundary" + } + }, + { + "family": "platform", + "method": "GET", + "path": "/api/account/cloud-health/export", + "operationId": "exportAccountCloudHealth", + "fingerprint": "42e49e8b5b1fd75cc5bdee057483a1fcd657c846701275a637862052e4317bbc", + "typescript": { + "status": "excluded", + "reason": "Console-only operation requires dashboard identity; not available to SDK credentials.", + "milestone": "credential-boundary" + } + }, { "family": "platform", "method": "GET", @@ -3111,7 +3305,7 @@ "method": "GET", "path": "/platform/call-opt-outs", "operationId": "listCallOptOuts", - "fingerprint": "c29dfe633323b8e56b0967a35e92a1969d34503326114b4e390259236d8186aa", + "fingerprint": "bb70f132ea96831cb1e18d5ef8de89324d8e0a057aea04c78ace7c3c91cd45df", "typescript": { "status": "covered", "method": "Client.callOptOuts.list" @@ -3122,7 +3316,7 @@ "method": "GET", "path": "/platform/call-policy", "operationId": "getCallPolicy", - "fingerprint": "86dd41e85ca27799e9ef18e94acb766ff78dc873c7f453ac6e9bdc3ed5f663e2", + "fingerprint": "9f438a1e553e489a45510e040cf54b638d2a654b6531682b8a3a3cbb97c44f9e", "typescript": { "status": "covered", "method": "Client.callPolicy.retrieve" @@ -3133,7 +3327,7 @@ "method": "GET", "path": "/platform/call-retention", "operationId": "getCallRetention", - "fingerprint": "06c1f95e5115d0c7d7c2f9f2d28b843d06f59a72f4c5a46fdc3d5f28cbb322da", + "fingerprint": "2b3c1250f64c3d0fb4a3a6023e2819573fb276b45eced67482957e6a6ebbf484", "typescript": { "status": "covered", "method": "Client.callRetention.retrieve" @@ -3150,12 +3344,24 @@ "method": "Client.calls.list" } }, + { + "family": "platform", + "method": "GET", + "path": "/platform/calls/{callId}", + "operationId": "getCallRecord", + "fingerprint": "f1a34d2b0028a96b067c770b5f798c662107b58723305ffa9d67dcda2c695c40", + "typescript": { + "status": "missing", + "reason": "Client.calls.retrieve: Implemented on SDK dev; not on this main-based branch until dev merges.", + "milestone": "sdk-dev-calls" + } + }, { "family": "platform", "method": "GET", "path": "/platform/calls/export", "operationId": "exportCallRecords", - "fingerprint": "4fdb20df1a28ecb1be5d59346cfbb1e6daca9178b21bc47205530747d3ce358f", + "fingerprint": "bc6ebd92416c61875542264c03e585afb978ed63fb7f218f1d89e53f56b01a86", "typescript": { "status": "covered", "method": "Client.calls.export" @@ -3177,7 +3383,7 @@ "method": "GET", "path": "/platform/campaigns", "operationId": "listPlatformCampaigns", - "fingerprint": "5b3da6a0dedd223a2445031682bd92ce2cd9d38ff5f0a87f93aadf68030d564c", + "fingerprint": "a103a7aff42594826b512cd5278567e1642d73e1ef7ec986f9a34e6c43c99bd3", "typescript": { "status": "covered", "method": "Client.campaigns.list" @@ -3188,7 +3394,7 @@ "method": "GET", "path": "/platform/campaigns/{campaignId}", "operationId": "getPlatformCampaign", - "fingerprint": "11bee4a4bab57d0bd7ea12d0db56c9ed2023e2ffd1e24f13c688fee9104f0484", + "fingerprint": "4f1c68c6b4beca679fb4ccfb17c3a34beff6d7964ab3722f471a8e5b2b8dee5b", "typescript": { "status": "covered", "method": "Client.campaigns.retrieve" @@ -3564,7 +3770,7 @@ "method": "GET", "path": "/platform/projects/{projectId}/events/stream", "operationId": "streamProjectEvents", - "fingerprint": "6197fbd6989c873ddbe6e5c945c82e39a8b3ee3a00eb8926ecb43c7430aa242c", + "fingerprint": "2f666e7807243987630475609796e432bc0cbd24c53c43811f6c9f26820cefd5", "typescript": { "status": "covered", "method": "Client.events.stream" @@ -3751,7 +3957,7 @@ "method": "GET", "path": "/platform/sessions/{session}/call-settings", "operationId": "getCallSettings", - "fingerprint": "7c2c41cd18b135b0d6cb0ce11bedd5a756897685a20039a0c9b81c56cf6e8a7b", + "fingerprint": "ea35e71edd7f02b04d981e6d0312413e16910e37add3f37942459f2e81476377", "typescript": { "status": "covered", "method": "MessagingClient.voip.retrieveCallSettings" @@ -3762,7 +3968,7 @@ "method": "GET", "path": "/platform/sessions/{session}/client-rules", "operationId": "getClientRules", - "fingerprint": "865abbf3829b3217056a65140f7b47f1af64031559f6dbf3d765f35c8c5945cf", + "fingerprint": "b0839bb9abf5b2964ac1635415970a246f69173c222c9c8e91f1745ddae89583", "typescript": { "status": "covered", "method": "MessagingClient.clientTokens.retrieveRules" @@ -3773,7 +3979,7 @@ "method": "GET", "path": "/platform/sessions/{sessionId}", "operationId": "getSession", - "fingerprint": "6b2f8f7f1496ad329d38cb2101521cf30cf9f75a9f882dc0556a3e24607f3f1c", + "fingerprint": "69e1d727b26f3c992abd4b1ec77a518c28846a95321a2f16ca0a22f9a6f0093f", "typescript": { "status": "covered", "method": "Client.sessions.retrieve" @@ -3784,7 +3990,7 @@ "method": "GET", "path": "/platform/sessions/{sessionId}/me", "operationId": "getSessionAccount", - "fingerprint": "b2c6e4b2a125054993ba08d2453edd2986caf8aa3deb690dfa51a28cbb32df32", + "fingerprint": "65783c7ca8a417faf88b1e7ecffe489f17a7bce9c4b89f0f7c1f679d436ed5e1", "typescript": { "status": "covered", "method": "MessagingClient.sessions.account" @@ -3817,7 +4023,7 @@ "method": "GET", "path": "/platform/sip-trunks", "operationId": "listSipTrunks", - "fingerprint": "1dcd5b0db6c5db32ee4a3473bb727ec55f7081eca98f91734e836a0fd6ec19f9", + "fingerprint": "9a5a9ef17a795465832fb624b860f0b97fc814cb3cb7acad0429cb0141b5086a", "typescript": { "status": "covered", "method": "Client.sipTrunks.list" @@ -3828,7 +4034,7 @@ "method": "GET", "path": "/platform/sip-trunks/{trunkId}", "operationId": "getSipTrunk", - "fingerprint": "6a73d5b4a51f45d35e122506a4762d2e8e0aeeccf918f75dca737cd18e5f9826", + "fingerprint": "8489724e85d9140c5804daaef711d8a7b7b8e322bfe89f18c1850d303d3e34a9", "typescript": { "status": "covered", "method": "Client.sipTrunks.retrieve" @@ -3839,7 +4045,7 @@ "method": "GET", "path": "/platform/sip/endpoint", "operationId": "getSipEndpoint", - "fingerprint": "fc06fe9600f15fdffcd6b5b115faaae377ff3229d6235b9fed62114ed22a00d9", + "fingerprint": "09d7595015af2449926b45554afdd0d310bb84ff71f5dddbd3bbf7662d8c86fb", "typescript": { "status": "covered", "method": "Client.sipTrunks.endpoint" @@ -3918,7 +4124,7 @@ "method": "GET", "path": "/platform/voice/audio", "operationId": "listVoiceAudio", - "fingerprint": "441531ea1a922ef65b8e43d84988f6e0a1916cffc325ad146cbd842a16fd7ad4", + "fingerprint": "9595e5c5c40a0f64446590aa4419bef0f5c2953fa35de21b0c8083f8d2f3bfde", "typescript": { "status": "covered", "method": "Client.voice.audio.list" @@ -3929,7 +4135,7 @@ "method": "GET", "path": "/platform/voice/audio/{assetId}", "operationId": "getVoiceAudio", - "fingerprint": "692966998fc60c01a79f8dc3dde724a6a0a7fbffd041cf58daa2514b82c21115", + "fingerprint": "bd5ebbb8faadf45c06ffd624aa123e4542af13aaf4dc6ee39d517722c8d1e2cc", "typescript": { "status": "covered", "method": "Client.voice.audio.retrieve" @@ -3940,7 +4146,7 @@ "method": "GET", "path": "/platform/voice/audio/{assetId}/preview", "operationId": "getVoiceAudioPreview", - "fingerprint": "a9e96bd7922b2377118eb001ee25da93ef777e4192e68da8b1289cbd8e0f9f96", + "fingerprint": "cbac58377ff758f57d156141c4b18c4e5d0c7141b696d34ac0906a13b2ca399d", "typescript": { "status": "covered", "method": "Client.voice.audio.previewUrl" @@ -3951,7 +4157,7 @@ "method": "GET", "path": "/platform/voice/provider-credentials", "operationId": "listVoiceProviderCredentials", - "fingerprint": "d3880ee66a9cb86cc09c5b7bd1539c055c778ffa8c74132fd641b130ad98aa02", + "fingerprint": "efc4699decea4a40f02b992eedccca76781b0a55611db84f1245b74784dbbc05", "typescript": { "status": "covered", "method": "Client.voice.providerCredentials.list" @@ -3962,7 +4168,7 @@ "method": "GET", "path": "/platform/voice/provider-credentials/{credentialId}", "operationId": "getVoiceProviderCredential", - "fingerprint": "3fc923e06ba764f92289880a0915e2014db432de101b210d4a914b2daa3b9c1e", + "fingerprint": "f213833fd63b1f476fc68e3f398a2ba1c705557c447b14760d7dad53210ff63a", "typescript": { "status": "covered", "method": "Client.voice.providerCredentials.retrieve" @@ -4116,7 +4322,7 @@ "method": "PATCH", "path": "/platform/campaigns/{campaignId}", "operationId": "updateCampaign", - "fingerprint": "b7af610a57c1f24db76241bd0bf60dce7528c2b4f6e7b2c9a9da68f23ab0d380", + "fingerprint": "9c0dd10a3f7f56b738a28de51816e6c4bc0015b8e7be74820897d17a580cb98e", "typescript": { "status": "covered", "method": "Client.campaigns.update" @@ -4183,7 +4389,7 @@ "method": "PATCH", "path": "/platform/sip-trunks/{trunkId}", "operationId": "updateSipTrunk", - "fingerprint": "6315e726ab8b64dd320a5bb25107eb8e6ec599fb39240f918b265326889d800c", + "fingerprint": "3e872fe2fd22309e112edf57f40df973377950ddf40802200970fc00ae170e62", "typescript": { "status": "covered", "method": "Client.sipTrunks.update" @@ -4206,7 +4412,7 @@ "method": "PATCH", "path": "/platform/voice/audio/{assetId}", "operationId": "updateVoiceAudio", - "fingerprint": "f0cff58ca5e16ca4c26e433eee8ad7274efeeee1f7641376dd697cafa5cb9b4b", + "fingerprint": "8c6eadcb2b9bf92dcdc4736c820c2716de747906cd05b5d1d30c9a169a1c615a", "typescript": { "status": "covered", "method": "Client.voice.audio.update" @@ -4228,7 +4434,7 @@ "method": "POST", "path": "/api/account/features/enrollment", "operationId": "setFeatureEnrollment", - "fingerprint": "d75db3cce485433757417e6823ce482850b681620e3aea984682eefea473f8dc", + "fingerprint": "1da2a7266a296a7bab7ef512bed2fe7e387700052cf633a86fa01213d938da97", "typescript": { "status": "excluded", "reason": "Dashboard identity and account controls require a live browser account session; not part of the machine SDK." @@ -4239,7 +4445,7 @@ "method": "POST", "path": "/api/account/features/evaluate", "operationId": "evaluateAccountFeatures", - "fingerprint": "65fea0db7dfde8d6ae7452fe139b7fc0ec89d46bfc2bf2cbd77cd7ad1a9a5b4e", + "fingerprint": "736aca34d47fc379d728898da21888471e3f9052e15fba97706e01d418c400a7", "typescript": { "status": "excluded", "reason": "Dashboard identity and account controls require a live browser account session; not part of the machine SDK." @@ -4690,7 +4896,7 @@ "method": "POST", "path": "/platform/call-opt-outs", "operationId": "createCallOptOut", - "fingerprint": "66e4e06f946a6391f4b7d1956de339f883009a50c4ec0efa9b93cdde367a7830", + "fingerprint": "4d20b153a8932597ee663ad13d8184cc484428d80adec3133a77fb8a7a3b5565", "typescript": { "status": "covered", "method": "Client.callOptOuts.create" @@ -4701,7 +4907,7 @@ "method": "POST", "path": "/platform/call-opt-outs/import", "operationId": "importCallOptOuts", - "fingerprint": "2d94c647923f4e66dd1323e3fede119a01b1644b26abf8cac16abce11099fa47", + "fingerprint": "7b3bc4e3a51ceb3ff80c4a2206fe595e307572db41dfd67e5e0de01d519057ee", "typescript": { "status": "covered", "method": "Client.callOptOuts.import" @@ -4712,7 +4918,7 @@ "method": "POST", "path": "/platform/campaigns", "operationId": "createPlatformCampaign", - "fingerprint": "78e845758e756676217319c79cd02e6cdaf9ebbb5b034e5fe3f04b87a1398fc7", + "fingerprint": "f2898a01845fdb18c3f1c0fed9131c11ce6e6825972934fccf57da36aa649a15", "typescript": { "status": "covered", "method": "Client.campaigns.create" @@ -4822,7 +5028,7 @@ "method": "POST", "path": "/platform/client-tokens", "operationId": "mintClientToken", - "fingerprint": "cac531399a855f9159b8686b033e6a55e099e9f681f8719f8b075b0d91bbe844", + "fingerprint": "496842f9335fa0c8525fe5843e7dee1e6d915bd950c6cb043c588b546142791b", "typescript": { "status": "covered", "method": "MessagingClient.clientTokens.mint" @@ -5043,7 +5249,7 @@ "method": "POST", "path": "/platform/projects/{projectId}/events/stream/{streamId}/ack", "operationId": "acknowledgeProjectEventStream", - "fingerprint": "5681f4181c434dc08e6da60f3d024d1e1ef8975e9e1f1d4b8093aa3e00031b4d", + "fingerprint": "5f8d0241ba5d221ac7d0374b718964b6b772513eefd31a2ff171ea152dd06102", "typescript": { "status": "covered", "method": "Client.events.acknowledgeStream" @@ -5142,7 +5348,7 @@ "method": "POST", "path": "/platform/sessions/{sessionId}/handoff-ack", "operationId": "sessionHandoffAck", - "fingerprint": "da3f871558dde7b0e566247931a2306cf2ec56326296b3e5051a16a377a9a5b6", + "fingerprint": "df9f352dd42e11c6f8f6d933026c1578720535ae46c2ce1a918acf1b8855246f", "typescript": { "status": "excluded", "reason": "Encrypted session handoff remains outside the public SDK until its dedicated security and atomicity contract is complete.", @@ -5154,7 +5360,7 @@ "method": "POST", "path": "/platform/sessions/{sessionId}/handoff-import", "operationId": "sessionHandoffImport", - "fingerprint": "a770d810789bcba6f8f599a7bf27c269670392fd9f4db5de898f5a7d579368df", + "fingerprint": "9fe3d3f06374c6e9af222380cab832dfb0965c35b144c56c6028793c2363c627", "typescript": { "status": "excluded", "reason": "Encrypted session handoff remains outside the public SDK until its dedicated security and atomicity contract is complete.", @@ -5166,7 +5372,7 @@ "method": "POST", "path": "/platform/sessions/{sessionId}/handoff-keys", "operationId": "sessionHandoffKeys", - "fingerprint": "7a2b66940c18b1ec2947ff2d085132128eff7bfb58f976c1456234d13a06d125", + "fingerprint": "b12d3b4f3b7119c46496a9c045a72de47c7546be9fbb9a5b0eca32e14d473811", "typescript": { "status": "excluded", "reason": "Encrypted session handoff remains outside the public SDK until its dedicated security and atomicity contract is complete.", @@ -5178,7 +5384,7 @@ "method": "POST", "path": "/platform/sessions/{sessionId}/logout", "operationId": "logoutSession", - "fingerprint": "6d3125bc828d3ada5baf9de74d9b3b7c20b159923ec9172dfce6ce88378c3569", + "fingerprint": "5b3209cae264ca724b3ced5d0fb743190b09e8ae63d82cea5cbfae6b37fb3ba5", "typescript": { "status": "covered", "method": "MessagingClient.sessions.logout" @@ -5189,7 +5395,7 @@ "method": "POST", "path": "/platform/sessions/{sessionId}/restart", "operationId": "restartSession", - "fingerprint": "c3c5385ad68c96388f927d5edf0976548b4d1dbd6a0a56b656038e0a0a489299", + "fingerprint": "70f96f166ef06e1e15e386189f3c16dce6855fd40ce47f98b42b16e62069e387", "typescript": { "status": "covered", "method": "MessagingClient.sessions.restart" @@ -5233,7 +5439,7 @@ "method": "POST", "path": "/platform/sessions/{sessionId}/upload-appstate-keys", "operationId": "sessionUploadAppStateKeys", - "fingerprint": "6e901c9f8f049dcebcf4cf551a78141cc485424565e86768afe284f2329dd9e8", + "fingerprint": "a733dd2c968ca1f8461ca9e659c938004a6204627abd75d460387a573dae7859", "typescript": { "status": "excluded", "reason": "Encrypted session handoff remains outside the public SDK until its dedicated security and atomicity contract is complete.", @@ -5267,7 +5473,7 @@ "method": "POST", "path": "/platform/sip-trunks", "operationId": "createSipTrunk", - "fingerprint": "329d5890f952398891e1068e21a570142f239fb80e92ce99e4cedd45765b3aa5", + "fingerprint": "fe89a2ae83b195b660fce4c3b979e6b159cb80bada6d337fcb0ba093363bfd92", "typescript": { "status": "covered", "method": "Client.sipTrunks.create" @@ -5278,7 +5484,7 @@ "method": "POST", "path": "/platform/sip-trunks/{trunkId}/credentials", "operationId": "rotateSipTrunkCredentials", - "fingerprint": "3f11f2b9edc9d3df1fd9373254660d46a4003a83c514bd46c538b39fb29eba5b", + "fingerprint": "9e9d1767de6505acd9c737038f2ff01c2e9113a23b3fc6c9a0d2ba4100f94ab7", "typescript": { "status": "covered", "method": "Client.sipTrunks.rotateCredentials" @@ -5301,7 +5507,7 @@ "method": "POST", "path": "/platform/voice/audio", "operationId": "createVoiceAudioUpload", - "fingerprint": "396a9d4824b7432de8c3a9ffa3d3dcbd1ca2a28382f66508de25f729ec8ea067", + "fingerprint": "9231c5cf356be150b0ce435c372752b16e6b858f6e0f4b7e7221e408b98f3e1a", "typescript": { "status": "covered", "method": "Client.voice.audio.createUpload" @@ -5312,7 +5518,7 @@ "method": "POST", "path": "/platform/voice/audio/{assetId}/complete", "operationId": "completeVoiceAudioUpload", - "fingerprint": "90bdd3c5b155f6ca7b6995d41fbd7f6829f308dad67cfebf1237ca55d37592b9", + "fingerprint": "a166635378e5fdf4d1302eedffbb56b3dc41eeee6bb332bf44765d0ea5b3e4d4", "typescript": { "status": "covered", "method": "Client.voice.audio.complete" @@ -5323,7 +5529,7 @@ "method": "POST", "path": "/platform/voice/audio/tts", "operationId": "synthesizeVoiceAudio", - "fingerprint": "07d35d10391d0c415b931d72be40f4ff379ea2c1a9612d0650f1e36448b8675a", + "fingerprint": "2e7b37f716405a98daa48475bffb25c0e0eaa4967ed63fc181648435f6155dd1", "typescript": { "status": "covered", "method": "Client.voice.audio.synthesize" @@ -5334,7 +5540,7 @@ "method": "POST", "path": "/platform/voice/provider-credentials", "operationId": "createVoiceProviderCredential", - "fingerprint": "e477c153316c4867166cb5f6cf76bafda9861ef84759f66ab58b9776d9633381", + "fingerprint": "fb66daca5fee50424db4551b62744d699c08907322639ee0b9df24684fe581ac", "typescript": { "status": "covered", "method": "Client.voice.providerCredentials.create" @@ -5345,7 +5551,7 @@ "method": "POST", "path": "/platform/voice/provider-credentials/{credentialId}/verify", "operationId": "verifyVoiceProviderCredential", - "fingerprint": "51f0135ffae48493414f934cff7eff9f7a8b7d88cc2a825d1a9f9d145793f5f3", + "fingerprint": "125c282e150e0339eb3d518a0fd335d9205748e91a013ad91df5cd1f52f5af4b", "typescript": { "status": "covered", "method": "Client.voice.providerCredentials.verify" @@ -5510,7 +5716,7 @@ "method": "PUT", "path": "/platform/call-policy", "operationId": "updateCallPolicy", - "fingerprint": "27eecc385aa3dc2fda442e5d213b0005d8bc4381c38c9ad17d8ce959c367ae75", + "fingerprint": "ed8f2cd61889fde7de23923757b58fb443c0f9a1883fcb0e563d1abd5c815797", "typescript": { "status": "covered", "method": "Client.callPolicy.update" @@ -5521,7 +5727,7 @@ "method": "PUT", "path": "/platform/call-retention", "operationId": "updateCallRetention", - "fingerprint": "f9055157722cdf330a3554c635e5a2ab15eac0779ecd08e5a491e598523063a2", + "fingerprint": "f880ca2ec21bcb19fc039fe5305e30be90773eaae9151bbfe5ed1398aa2d865c", "typescript": { "status": "covered", "method": "Client.callRetention.update" @@ -5620,7 +5826,7 @@ "method": "PUT", "path": "/platform/sessions/{session}/call-settings", "operationId": "updateCallSettings", - "fingerprint": "a19912633c51d8faab3fc1eaf94b51c4bce6c5bc73c55ba307c202f0fac9cd22", + "fingerprint": "ab423fca728b77bad57131b83049881b765409c1a37135422ba73a234555f28d", "typescript": { "status": "covered", "method": "MessagingClient.voip.updateCallSettings" @@ -5631,7 +5837,7 @@ "method": "PUT", "path": "/platform/sessions/{session}/client-rules", "operationId": "setClientRules", - "fingerprint": "ef3d20c9aecf22a9c46e8a8a812a247e20d52a2acc988714ae0c78d79c5b4c53", + "fingerprint": "03a890d1da1aef6817598f349b28856eb091cd5bb2e28a698aacdb65c1ab0b39", "typescript": { "status": "covered", "method": "MessagingClient.clientTokens.updateRules" @@ -5642,7 +5848,7 @@ "method": "PUT", "path": "/platform/sessions/{sessionId}", "operationId": "updateSession", - "fingerprint": "cb8c0c0a85e4e798694eb76a2df436cfc091602542686f5b586718eae348816a", + "fingerprint": "a3af01e8b9c23b5beb84f9cfbd14cf6b6969fd027e35cc8b4f47d924cfdda320", "typescript": { "status": "covered", "method": "Client.sessions.update" diff --git a/contracts/openapi.messaging.json b/contracts/openapi.messaging.json index f959f015..c2404bfd 100644 --- a/contracts/openapi.messaging.json +++ b/contracts/openapi.messaging.json @@ -173,6 +173,9 @@ "location", "contact", "phone_number_shared", + "native_flow_response", + "button_reply", + "list_reply", "poll", "sticker", "reaction", @@ -246,6 +249,36 @@ "paramsJson" ] }, + "replyChoice": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "button", + "list" + ], + "description": "Reply control the contact used: a reply or quick-reply button, or a list row." + }, + "id": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "ID you assigned to the selected button or list row. It is never the visible label, except for a template quick-reply button sent without a payload, where WhatsApp returns the button text." + } + }, + "required": [ + "kind", + "id" + ], + "description": "Present when the contact selects a reply button or list row." + }, + "parentMessageId": { + "type": "string", + "minLength": 1, + "maxLength": 128, + "description": "WhatsApp ID of the message this one quotes, when WhatsApp supplies one. It is not necessarily a message you sent." + }, "media": { "type": "string" }, @@ -333,6 +366,36 @@ "paramsJson" ] }, + "replyChoice": { + "type": "object", + "properties": { + "kind": { + "type": "string", + "enum": [ + "button", + "list" + ], + "description": "Reply control the contact used: a reply or quick-reply button, or a list row." + }, + "id": { + "type": "string", + "minLength": 1, + "maxLength": 256, + "description": "ID you assigned to the selected button or list row. It is never the visible label, except for a template quick-reply button sent without a payload, where WhatsApp returns the button text." + } + }, + "required": [ + "kind", + "id" + ], + "description": "Present when the contact selects a reply button or list row." + }, + "parentMessageId": { + "type": "string", + "minLength": 1, + "maxLength": 128, + "description": "WhatsApp ID of the message this one quotes, when WhatsApp supplies one. It is not necessarily a message you sent." + }, "interactive": { "type": "object", "additionalProperties": {} @@ -479,6 +542,31 @@ "payload" ] }, + "MetaPricingReport": { + "type": "object", + "properties": { + "billable": { + "type": "boolean", + "description": "Whether WhatsApp reported the message as billable. WhatsApp is deprecating this field; use `type`." + }, + "pricing_model": { + "type": "string", + "description": "WhatsApp pricing model, for example `PMP` (per-message pricing).", + "example": "PMP" + }, + "category": { + "type": "string", + "description": "WhatsApp pricing category, for example `marketing`, `utility`, `authentication` or `service`.", + "example": "utility" + }, + "type": { + "type": "string", + "description": "WhatsApp pricing type, for example `regular`, `free_customer_service` or `free_entry_point`.", + "example": "free_customer_service" + } + }, + "description": "Official API only. The pricing classification WhatsApp reported for this message, copied as reported. Meta bills your WhatsApp Business Account directly; this is not a Polymorfa charge and contains no price." + }, "MessageAckPayload": { "type": "object", "properties": { @@ -525,6 +613,9 @@ }, "timestamp": { "type": "number" + }, + "pricing": { + "$ref": "#/components/schemas/MetaPricingReport" } }, "required": [ @@ -2338,7 +2429,7 @@ }, "codec": { "type": "string", - "description": "Negotiated audio codec's bare subtype; empty when no codec stats.", + "description": "Negotiated audio codec's bare subtype; empty when the audio stream does not identify its codec.", "example": "opus" }, "jitterMs": { @@ -2437,6 +2528,10 @@ "username": { "type": "string" }, + "handRaised": { + "type": "boolean", + "description": "Whether this participant has raised their hand when reported by the call roster." + }, "audioMuted": { "type": "boolean", "enum": [ @@ -5914,21 +6009,21 @@ "type": "number", "description": "Credit quantity this number consumed inside the window, with up to six decimal places.", "minimum": 0, - "multipleOf": 0.000001, + "multipleOf": 1e-06, "example": 1080 }, "capCents": { "type": "number", "description": "Maximum credit quantity this claim can return, with up to six decimal places.", "minimum": 0, - "multipleOf": 0.000001, + "multipleOf": 1e-06, "example": 1080 }, "amountCents": { "type": "number", "description": "Credit quantity this claim returns, with up to six decimal places.", "minimum": 0, - "multipleOf": 0.000001, + "multipleOf": 1e-06, "example": 1080 }, "summary": { @@ -9346,7 +9441,7 @@ "properties": { "id": { "type": "string", - "description": "Media ID. Download it with `GET /messaging/media/{id}` and a credential with `media:read`." + "description": "Media ID. Download the bytes from `url` with a credential that has `media:read`." }, "mimeType": { "type": "string" @@ -9368,6 +9463,34 @@ "url" ] }, + "HistoryMediaRetrieval": { + "type": "object", + "properties": { + "state": { + "type": "string", + "enum": [ + "pending", + "stored", + "unavailable", + "expired", + "too_large", + "unsupported_type", + "failed", + "cancelled" + ], + "description": "`pending`: the copy is not stored yet. `stored`: `media` lists the copy. Every other state is final and no copy will be stored." + }, + "reason": { + "type": "string", + "description": "Why a final state other than `stored` was reached, for example `provider_media_not_found` or `media_too_large`.", + "example": "provider_media_expired" + } + }, + "required": [ + "state" + ], + "description": "Official API media only: whether a copy of the media was stored with this message." + }, "HistoryMessage": { "type": "object", "properties": { @@ -9482,6 +9605,9 @@ "$ref": "#/components/schemas/HistoryMedia" }, "description": "Downloadable media for image, video, audio, document, and sticker messages. Empty when no copy is available." + }, + "mediaRetrieval": { + "$ref": "#/components/schemas/HistoryMediaRetrieval" } }, "required": [ @@ -9549,6 +9675,167 @@ "data" ] }, + "OfficialServiceWindow": { + "type": "object", + "properties": { + "state": { + "type": "string", + "enum": [ + "open", + "closed", + "unknown" + ], + "description": "`open`: the person messaged, reacted or called this Number through the Official API in the last 24 hours, so free-form messages can be sent. `closed`: no such activity in the last 24 hours; only approved templates can be sent, and Polymorfa refuses free-form sends with `conversation_window_closed`. `unknown`: Polymorfa cannot prove the window is closed; sends are passed to WhatsApp, which decides." + }, + "reason": { + "type": "string", + "nullable": true, + "enum": [ + "not_tracked", + "tracking_started", + "notifications_interrupted", + "identity_unlinked", + null + ], + "description": "Why the state is `unknown`, otherwise null. `not_tracked`: no WhatsApp notification has been processed for this connection yet. `tracking_started`: tracking began less than 24 hours ago. `notifications_interrupted`: a WhatsApp notification could not be processed in the last 24 hours. `identity_unlinked`: someone without a phone number (or username ID) wrote in during the last 24 hours and could be this person." + }, + "openedAt": { + "type": "string", + "nullable": true, + "format": "date-time", + "description": "When the person's latest known message, reaction or call started or reset the window. Null when none is known." + }, + "expiresAt": { + "type": "string", + "nullable": true, + "format": "date-time", + "description": "`openedAt` plus 24 hours. In the past when the window has closed." + }, + "checkedAt": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "state", + "reason", + "openedAt", + "expiresAt", + "checkedAt" + ] + }, + "GetCustomerServiceWindowResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "data": { + "$ref": "#/components/schemas/OfficialServiceWindow" + } + }, + "required": [ + "success", + "data" + ] + }, + "MetaPricingGroup": { + "type": "object", + "properties": { + "category": { + "type": "string", + "description": "Pricing category as WhatsApp reported it, for example `marketing`, `utility`, `authentication` or `service`.", + "example": "utility" + }, + "pricingModel": { + "type": "string", + "description": "Pricing model as WhatsApp reported it, for example `PMP`.", + "example": "PMP" + }, + "pricingType": { + "type": "string", + "nullable": true, + "description": "Pricing type as WhatsApp reported it, for example `regular`, `free_customer_service` or `free_entry_point`. Null when not reported.", + "example": "regular" + }, + "billable": { + "type": "boolean", + "nullable": true, + "description": "The `billable` flag as WhatsApp reported it. Null when not reported." + }, + "messages": { + "type": "integer", + "minimum": 0, + "description": "Messages WhatsApp reported with this classification." + } + }, + "required": [ + "category", + "pricingModel", + "pricingType", + "billable", + "messages" + ] + }, + "MetaPricingSummary": { + "type": "object", + "properties": { + "source": { + "type": "string", + "enum": [ + "meta" + ], + "description": "Always `meta`: these are WhatsApp's own reports. Meta bills your WhatsApp Business Account directly. They are not a Polymorfa charge and contain no prices." + }, + "since": { + "type": "string", + "format": "date-time" + }, + "until": { + "type": "string", + "format": "date-time" + }, + "messages": { + "type": "integer", + "minimum": 0, + "description": "Messages with a WhatsApp pricing report in the period." + }, + "groups": { + "type": "array", + "items": { + "$ref": "#/components/schemas/MetaPricingGroup" + } + } + }, + "required": [ + "source", + "since", + "until", + "messages", + "groups" + ] + }, + "GetMetaPricingSummaryResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "data": { + "$ref": "#/components/schemas/MetaPricingSummary" + } + }, + "required": [ + "success", + "data" + ] + }, "RejectCallRequest": { "type": "object", "properties": { @@ -10055,6 +10342,20 @@ "pattern": "^(?:\\+[1-9]\\d{1,14}|[1-9][0-9]{0,18})$", "example": "+15550100" }, + "groupId": { + "type": "string", + "pattern": "^[1-9][0-9]{0,18}$" + }, + "participants": { + "type": "array", + "items": { + "type": "string", + "pattern": "^(?:\\+[1-9]\\d{1,14}|[1-9][0-9]{0,18})$" + }, + "minItems": 2, + "maxItems": 31, + "uniqueItems": true + }, "video": { "type": "boolean" }, @@ -10069,8 +10370,65 @@ "example": "agent-7" } }, - "required": [ - "to" + "description": "Provide exactly one of to, participants or groupId. Group calls require a Linked Device number and 2 to 31 remote recipients.", + "oneOf": [ + { + "required": [ + "to" + ], + "not": { + "anyOf": [ + { + "required": [ + "participants" + ] + }, + { + "required": [ + "groupId" + ] + } + ] + } + }, + { + "required": [ + "participants" + ], + "not": { + "anyOf": [ + { + "required": [ + "to" + ] + }, + { + "required": [ + "groupId" + ] + } + ] + } + }, + { + "required": [ + "groupId" + ], + "not": { + "anyOf": [ + { + "required": [ + "to" + ] + }, + { + "required": [ + "participants" + ] + } + ] + } + } ] }, "VoipAnswerResult": { @@ -10196,6 +10554,10 @@ "connected", "left" ] + }, + "handRaised": { + "type": "boolean", + "description": "Whether this participant has raised their hand." } }, "required": [ @@ -15522,6 +15884,174 @@ "data" ] }, + "WaTemplate": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "tenantId": { + "type": "string" + }, + "session": { + "type": "string" + }, + "wabaId": { + "type": "string" + }, + "name": { + "type": "string" + }, + "language": { + "type": "string" + }, + "category": { + "type": "string", + "enum": [ + "UTILITY", + "MARKETING", + "AUTHENTICATION" + ] + }, + "status": { + "type": "string", + "enum": [ + "PENDING", + "APPROVED", + "REJECTED", + "PAUSED", + "DISABLED", + "DELETED", + "ARCHIVED", + "IN_APPEAL", + "LIMIT_EXCEEDED", + "PENDING_DELETION" + ] + }, + "components": { + "type": "array", + "items": {} + }, + "metaTemplateId": { + "type": "string" + }, + "rejectionReason": { + "type": "string" + }, + "qualityScore": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "updatedAt": { + "type": "string" + } + }, + "required": [ + "id", + "tenantId", + "session", + "wabaId", + "name", + "language", + "category", + "status", + "components", + "createdAt", + "updatedAt" + ] + }, + "ListTemplatesResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/WaTemplate" + } + } + }, + "required": [ + "success", + "data" + ] + }, + "GetTemplateResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "data": { + "$ref": "#/components/schemas/WaTemplate" + } + }, + "required": [ + "success", + "data" + ] + }, + "CreateTemplateResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "data": { + "$ref": "#/components/schemas/WaTemplate" + } + }, + "required": [ + "success", + "data" + ] + }, + "CreateTemplateRequest": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "pattern": "^[a-z0-9_]+$" + }, + "language": { + "type": "string", + "minLength": 2 + }, + "category": { + "type": "string", + "enum": [ + "UTILITY", + "MARKETING", + "AUTHENTICATION" + ] + }, + "components": { + "type": "array", + "items": {}, + "minItems": 1 + } + }, + "required": [ + "name", + "language", + "category", + "components" + ] + }, "MessageTemplate": { "type": "object", "properties": { @@ -15758,6 +16288,92 @@ "session" ] }, + "CampaignSendWindowRange": { + "type": "object", + "properties": { + "start": { + "type": "string", + "pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$", + "description": "Local start time, `HH:MM` in 24-hour time, inclusive.", + "example": "09:00" + }, + "end": { + "type": "string", + "pattern": "^(([01][0-9]|2[0-3]):[0-5][0-9]|24:00)$", + "description": "Local end time, `HH:MM` in 24-hour time or `24:00`, exclusive. Must be later than `start`.", + "example": "18:00" + } + }, + "required": [ + "start", + "end" + ], + "additionalProperties": false + }, + "CampaignSendWindow": { + "type": "object", + "nullable": true, + "properties": { + "timeZone": { + "type": "string", + "description": "IANA time zone the window is written in.", + "example": "America/Sao_Paulo" + }, + "days": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "monday", + "tuesday", + "wednesday", + "thursday", + "friday", + "saturday", + "sunday" + ] + }, + "minItems": 1, + "maxItems": 7, + "uniqueItems": true, + "description": "Weekdays the campaign may send on, in the window's time zone. At least one; no repeats.", + "example": [ + "monday", + "tuesday", + "wednesday", + "thursday", + "friday" + ] + }, + "hours": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CampaignSendWindowRange" + }, + "minItems": 1, + "maxItems": 4, + "description": "Local time ranges the campaign may send in on those days, at most 4. Ranges must not overlap. A range cannot cross midnight; use two ranges instead." + }, + "recipientTimeZone": { + "type": "boolean", + "description": "Evaluate the window in each recipient's own time zone. The zone comes from the recipient variable named by `timeZoneVariable` when it holds a valid IANA zone, otherwise from the phone number's country when that country uses one UTC offset, otherwise `timeZone`." + }, + "timeZoneVariable": { + "type": "string", + "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.-]{0,63}$", + "description": "Recipient variable holding the recipient's IANA time zone, used when `recipientTimeZone` is true.", + "example": "timeZone" + } + }, + "required": [ + "timeZone", + "days", + "hours", + "recipientTimeZone", + "timeZoneVariable" + ], + "description": "When the campaign may send; null sends at any time." + }, "Campaign": { "type": "object", "properties": { @@ -15813,6 +16429,9 @@ }, "updatedAt": { "type": "number" + }, + "sendWindow": { + "$ref": "#/components/schemas/CampaignSendWindow" } }, "required": [ @@ -15831,7 +16450,8 @@ "launchedAt", "completedAt", "createdAt", - "updatedAt" + "updatedAt", + "sendWindow" ] }, "ListCampaignsResponse": { @@ -15910,6 +16530,72 @@ "data" ] }, + "CampaignSendWindowRequest": { + "type": "object", + "nullable": true, + "properties": { + "timeZone": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "description": "IANA time zone the window is written in, such as `America/Sao_Paulo`. When omitted, the team's time zone is stored, or `UTC` when the team has none.", + "example": "America/Sao_Paulo" + }, + "days": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "monday", + "tuesday", + "wednesday", + "thursday", + "friday", + "saturday", + "sunday" + ] + }, + "minItems": 1, + "maxItems": 7, + "uniqueItems": true, + "description": "Weekdays the campaign may send on, in the window's time zone. At least one; no repeats.", + "example": [ + "monday", + "tuesday", + "wednesday", + "thursday", + "friday" + ] + }, + "hours": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CampaignSendWindowRange" + }, + "minItems": 1, + "maxItems": 4, + "description": "Local time ranges the campaign may send in on those days, at most 4. Ranges must not overlap. A range cannot cross midnight; use two ranges instead." + }, + "recipientTimeZone": { + "type": "boolean", + "default": false, + "description": "Evaluate the window in each recipient's own time zone. The zone comes from the recipient variable named by `timeZoneVariable` when it holds a valid IANA zone, otherwise from the phone number's country when that country uses one UTC offset, otherwise `timeZone`." + }, + "timeZoneVariable": { + "type": "string", + "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.-]{0,63}$", + "default": "timeZone", + "description": "Recipient variable holding the recipient's IANA time zone, used when `recipientTimeZone` is true.", + "example": "timeZone" + } + }, + "required": [ + "days", + "hours" + ], + "additionalProperties": false, + "description": "When the campaign may send. Recipients outside the window stay `queued` and are sent when it next opens. `null` sends at any time." + }, "CampaignRecipientInput": { "type": "object", "properties": { @@ -15961,6 +16647,9 @@ "scheduledAt": { "type": "number" }, + "sendWindow": { + "$ref": "#/components/schemas/CampaignSendWindowRequest" + }, "recipients": { "type": "array", "items": { @@ -16018,6 +16707,17 @@ "minimum": 0, "maximum": 8640000000000000, "description": "Start time in milliseconds since the Unix epoch, or null to start at launch. Only an unlaunched draft can change it." + }, + "sendWindow": { + "nullable": true, + "allOf": [ + { + "$ref": "#/components/schemas/CampaignSendWindowRequest" + }, + { + "description": "When the campaign may send. Null removes the window. Only a draft or paused campaign can change it; a paused edit requires in-flight sends to have settled." + } + ] } }, "additionalProperties": false, @@ -16431,7 +17131,8 @@ "type": "string", "enum": [ "initial", - "add_connection" + "add_connection", + "reauthorization" ] }, "connectionGoal": { @@ -16873,7 +17574,8 @@ "type": "string", "enum": [ "initial", - "add_connection" + "add_connection", + "reauthorization" ] }, "connectionGoal": { @@ -17011,6 +17713,171 @@ "data" ] }, + "CloudCredentialHealth": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "pending", + "healthy", + "action_required", + "unknown" + ], + "description": "`healthy`: every check passed. `action_required`: a check failed and the Number needs reauthorization or a change in Meta. `unknown`: Meta could not be reached or returned an incomplete answer; the check is retried. `pending`: not checked since the credentials were saved." + }, + "checkedAt": { + "type": "string", + "nullable": true, + "format": "date-time", + "description": "When the last validation completed, or null before the first one." + }, + "nextCheckAt": { + "type": "string", + "nullable": true, + "format": "date-time", + "description": "When the next scheduled validation becomes due." + }, + "token": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "valid", + "expired", + "invalid", + "unknown" + ] + }, + "expiresAt": { + "type": "string", + "nullable": true, + "format": "date-time", + "description": "Expiry reported by Meta; null for a token without an expiry or when unknown." + } + }, + "required": [ + "status", + "expiresAt" + ] + }, + "missingPermissions": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "whatsapp_business_messaging", + "whatsapp_business_management" + ] + }, + "description": "Required permissions the saved token lacks for this WhatsApp Business Account." + }, + "phoneRegistration": { + "type": "string", + "enum": [ + "registered", + "not_registered", + "unknown" + ], + "description": "Whether Meta reports the phone number as connected to the Cloud API." + }, + "webhookSubscription": { + "type": "string", + "enum": [ + "subscribed", + "not_subscribed", + "unknown" + ], + "description": "Whether the app that issued the token is subscribed to the WhatsApp Business Account's webhooks." + }, + "failureCode": { + "type": "string", + "nullable": true, + "enum": [ + "app_credentials_rejected", + "token_expired", + "token_invalid", + "token_app_mismatch", + "permission_missing", + "phone_not_registered", + "webhook_not_subscribed", + "credentials_unreadable", + "meta_unavailable", + "meta_response_invalid", + null + ], + "description": "The first failed or inconclusive check, or null when healthy." + } + }, + "required": [ + "status", + "checkedAt", + "nextCheckAt", + "token", + "missingPermissions", + "phoneRegistration", + "webhookSubscription", + "failureCode" + ] + }, + "CloudCredentialHealthResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "data": { + "$ref": "#/components/schemas/CloudCredentialHealth" + } + }, + "required": [ + "success", + "data" + ] + }, + "CloudReauthorization": { + "type": "object", + "properties": { + "quicklinkId": { + "type": "string", + "description": "QuickLink identifier, prefixed `ql_`. Read its progress with `GET /messaging/quicklinks/{id}`." + }, + "url": { + "type": "string", + "description": "Hosted link. Share it only with the person who manages the Number's Meta assets." + }, + "session": { + "type": "string" + } + }, + "required": [ + "quicklinkId", + "url", + "session" + ] + }, + "CloudReauthorizationResponse": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "data": { + "$ref": "#/components/schemas/CloudReauthorization" + } + }, + "required": [ + "success", + "data" + ] + }, "PublicQuickLinkError": { "type": "object", "properties": { @@ -17256,7 +18123,8 @@ "type": "string", "enum": [ "initial", - "add_connection" + "add_connection", + "reauthorization" ] }, "connectionGoal": { @@ -17907,6 +18775,213 @@ "event" ], "additionalProperties": false + }, + "VoipReactionRequest": { + "type": "object", + "additionalProperties": false, + "properties": { + "connectionId": { + "type": "string", + "pattern": "^[A-Za-z0-9_-]{8,64}$" + }, + "participant": { + "type": "string", + "pattern": "^[A-Za-z0-9._:@-]{1,128}$", + "description": "Server credentials only. Must own the connection." + }, + "emoji": { + "type": "string", + "enum": [ + "", + "👍", + "❤️", + "😂", + "😮", + "😢", + "🙏" + ], + "description": "Empty clears the previous reaction." + } + }, + "required": [ + "connectionId", + "emoji" + ] + }, + "VoipHandRequest": { + "type": "object", + "additionalProperties": false, + "properties": { + "connectionId": { + "type": "string", + "pattern": "^[A-Za-z0-9_-]{8,64}$" + }, + "participant": { + "type": "string", + "pattern": "^[A-Za-z0-9._:@-]{1,128}$", + "description": "Server credentials only. Must own the connection." + }, + "raised": { + "type": "boolean" + } + }, + "required": [ + "connectionId", + "raised" + ] + }, + "VoipPreviewedCallLink": { + "type": "object", + "properties": { + "session": { + "type": "string" + }, + "video": { + "type": "boolean" + }, + "creator": { + "$ref": "#/components/schemas/ConversationIdentity" + }, + "approvalRequired": { + "type": "boolean" + }, + "isAdmin": { + "type": "boolean", + "description": "Whether WhatsApp reports this Number as a link administrator. This field does not grant API permissions." + } + }, + "required": [ + "session", + "video", + "creator", + "approvalRequired", + "isAdmin" + ] + }, + "VoipPreviewedCallLinkEnvelope": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "data": { + "$ref": "#/components/schemas/VoipPreviewedCallLink" + } + }, + "required": [ + "success", + "data" + ] + }, + "VoipCreatedCallLink": { + "type": "object", + "properties": { + "session": { + "type": "string" + }, + "token": { + "type": "string" + }, + "url": { + "type": "string", + "format": "uri" + }, + "video": { + "type": "boolean" + } + }, + "required": [ + "session", + "token", + "url", + "video" + ] + }, + "VoipCreateCallLinkRequest": { + "type": "object", + "properties": { + "session": { + "type": "string", + "minLength": 1, + "maxLength": 128 + }, + "video": { + "type": "boolean", + "description": "Create a video link. Defaults to false (voice)." + } + }, + "required": [ + "session" + ], + "additionalProperties": false + }, + "VoipPreviewCallLinkRequest": { + "type": "object", + "properties": { + "session": { + "type": "string", + "minLength": 1, + "maxLength": 128 + }, + "token": { + "type": "string", + "pattern": "^[A-Za-z0-9_-]{1,256}$", + "description": "Private token from the WhatsApp call link. Send it only in the request body." + }, + "video": { + "type": "boolean", + "description": "True for a video link, false for a voice link. Must match the link." + } + }, + "required": [ + "session", + "token" + ], + "additionalProperties": false + }, + "VoipCreatedCallLinkEnvelope": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "data": { + "$ref": "#/components/schemas/VoipCreatedCallLink" + } + }, + "required": [ + "success", + "data" + ] + } + }, + "headers": { + "X-RateLimit-Limit": { + "description": "Team request quota per rolling minute, when the per-team request limiter applies.", + "schema": { + "type": "integer", + "minimum": 1 + } + }, + "X-RateLimit-Remaining": { + "description": "Requests left in the per-team quota; zero when this limiter rejects the request.", + "schema": { + "type": "integer", + "minimum": 0 + } + }, + "X-RateLimit-Reset": { + "description": "Unix timestamp in seconds when the oldest counted request leaves the rolling window, when the per-team request limiter applies. Follow Retry-After on a rejected request.", + "schema": { + "type": "integer", + "minimum": 0 + } } }, "securitySchemes": { @@ -17989,6 +19064,17 @@ "$ref": "#/components/schemas/VersionResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } } @@ -18011,6 +19097,17 @@ "$ref": "#/components/schemas/StatusResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } } @@ -18080,6 +19177,17 @@ ] } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -18101,6 +19209,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { @@ -18122,6 +19241,17 @@ "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -18272,10 +19402,32 @@ ] } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { "description": "Invalid feature request", + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + }, "content": { "application/json": { "schema": { @@ -18297,6 +19449,17 @@ }, "401": { "description": "Authentication required", + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + }, "content": { "application/json": { "schema": { @@ -18318,6 +19481,17 @@ }, "403": { "description": "Credential cannot access feature evaluation", + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + }, "content": { "application/json": { "schema": { @@ -18339,6 +19513,17 @@ }, "413": { "description": "Request exceeds 16 KiB", + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + }, "content": { "application/json": { "schema": { @@ -18368,7 +19553,7 @@ "Pairing" ], "summary": "Get QR code for device pairing", - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `sessions:manage`\n\nDirect-pairing access must cover this Number. It may be included in an Enterprise or custom agreement. Use [QuickLink](/integrations/quicklink) when direct pairing is unavailable. Requests without access return 403.", + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `sessions:manage`\n\nDirect-pairing access must be enabled for the team that owns this Number. Use [QuickLink](/integrations/quicklink) when direct pairing is unavailable. Requests without access return 403.", "security": [ { "BearerAuth": [] @@ -18413,6 +19598,17 @@ "format": "binary" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -18434,6 +19630,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -18455,6 +19662,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -18476,6 +19694,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -18497,6 +19726,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -18518,6 +19758,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -18536,7 +19787,7 @@ "Pairing" ], "summary": "Request a pairing code for a phone number", - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `sessions:manage`\n\nDirect-pairing access must cover this Number. It may be included in an Enterprise or custom agreement. Use [QuickLink](/integrations/quicklink) when direct pairing is unavailable. Requests without access return 403.", + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `sessions:manage`\n\nDirect-pairing access must be enabled for the team that owns this Number. Use [QuickLink](/integrations/quicklink) when direct pairing is unavailable. Requests without access return 403.", "security": [ { "BearerAuth": [] @@ -18571,6 +19822,17 @@ "$ref": "#/components/schemas/RequestPairCodeResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -18592,6 +19854,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -18613,6 +19886,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -18634,6 +19918,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -18655,6 +19950,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -18676,6 +19982,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -18743,6 +20060,17 @@ "$ref": "#/components/schemas/HybridRoutingPolicyResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -18753,6 +20081,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -18763,6 +20102,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -18773,6 +20123,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -18783,6 +20144,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { @@ -18793,6 +20165,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -18803,6 +20186,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { @@ -18813,6 +20207,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -18885,6 +20290,17 @@ "$ref": "#/components/schemas/HybridRoutingPolicyResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -18895,6 +20311,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -18905,6 +20332,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -18915,6 +20353,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -18925,6 +20374,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { @@ -18935,6 +20395,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -18945,6 +20416,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { @@ -18955,6 +20437,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -18997,6 +20490,17 @@ "$ref": "#/components/schemas/HybridLinkStateResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -19007,6 +20511,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -19017,6 +20532,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -19027,6 +20553,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -19037,6 +20574,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { @@ -19047,6 +20595,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -19057,6 +20616,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { @@ -19067,6 +20637,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -19133,6 +20714,17 @@ "$ref": "#/components/schemas/HybridLinkPauseResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -19143,6 +20735,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -19153,6 +20756,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -19163,6 +20777,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -19173,6 +20798,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { @@ -19183,6 +20819,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -19193,6 +20840,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { @@ -19203,6 +20861,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -19300,6 +20969,15 @@ "schema": { "type": "string" } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -19335,6 +21013,15 @@ "schema": { "type": "string" } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -19346,6 +21033,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -19356,6 +21054,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "402": { @@ -19366,6 +21075,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -19376,6 +21096,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -19386,6 +21117,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { @@ -19436,6 +21178,15 @@ "schema": { "type": "string" } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -19447,6 +21198,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "429": { @@ -19467,6 +21229,15 @@ "call_permission_request" ] } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } }, "content": { @@ -19509,6 +21280,15 @@ "schema": { "type": "string" } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -19544,6 +21324,15 @@ "schema": { "type": "string" } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -19579,6 +21368,15 @@ "schema": { "type": "string" } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -19614,6 +21412,15 @@ "schema": { "type": "string" } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } } @@ -19669,6 +21476,17 @@ "$ref": "#/components/schemas/MessageActionResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "202": { @@ -19679,6 +21497,17 @@ "$ref": "#/components/schemas/AsyncAcceptedResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -19689,6 +21518,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -19699,6 +21539,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "402": { @@ -19709,6 +21560,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -19719,6 +21581,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -19729,6 +21602,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { @@ -19739,6 +21623,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -19749,6 +21644,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "502": { @@ -19759,6 +21665,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { @@ -19769,6 +21686,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "504": { @@ -19779,6 +21707,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -19834,6 +21773,17 @@ "$ref": "#/components/schemas/MessageActionResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "202": { @@ -19844,6 +21794,17 @@ "$ref": "#/components/schemas/AsyncAcceptedResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -19854,6 +21815,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -19864,6 +21836,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "402": { @@ -19874,6 +21857,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -19884,6 +21878,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -19894,6 +21899,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { @@ -19904,6 +21920,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -19914,6 +21941,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "502": { @@ -19924,6 +21962,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { @@ -19934,6 +21983,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "504": { @@ -19944,6 +22004,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -20032,6 +22103,15 @@ "schema": { "type": "string" } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -20067,6 +22147,15 @@ "schema": { "type": "string" } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -20078,6 +22167,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -20088,6 +22188,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "402": { @@ -20098,6 +22209,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -20108,6 +22230,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -20118,6 +22251,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { @@ -20168,81 +22312,20 @@ "schema": { "type": "string" } - } - } - }, - "500": { - "description": "Unexpected service failure", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - } - } - }, - "headers": { - "X-Polymorfa-Operation-Id": { - "description": "Hybrid operation ID when a send is reserved. Read its status after an uncertain result; do not submit a new key.", - "schema": { - "type": "string", - "format": "uuid" - } }, - "X-Polymorfa-Transport": { - "description": "Transport selected by Hybrid routing, when a route was admitted.", - "schema": { - "type": "string", - "enum": [ - "linked_devices", - "official_api" - ] - } + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" }, - "X-Polymorfa-Routing-Reason": { - "description": "Reason for the selected Hybrid route, when a route was admitted.", - "schema": { - "type": "string" - } - } - } - }, - "502": { - "description": "Messaging provider failure", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - } - } - }, - "headers": { - "X-Polymorfa-Operation-Id": { - "description": "Hybrid operation ID when a send is reserved. Read its status after an uncertain result; do not submit a new key.", - "schema": { - "type": "string", - "format": "uuid" - } - }, - "X-Polymorfa-Transport": { - "description": "Transport selected by Hybrid routing, when a route was admitted.", - "schema": { - "type": "string", - "enum": [ - "linked_devices", - "official_api" - ] - } + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" }, - "X-Polymorfa-Routing-Reason": { - "description": "Reason for the selected Hybrid route, when a route was admitted.", - "schema": { - "type": "string" - } + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, - "503": { - "description": "Session or service unavailable", + "500": { + "description": "Unexpected service failure", "content": { "application/json": { "schema": { @@ -20273,6 +22356,103 @@ "schema": { "type": "string" } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "502": { + "description": "Messaging provider failure", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + } + } + }, + "headers": { + "X-Polymorfa-Operation-Id": { + "description": "Hybrid operation ID when a send is reserved. Read its status after an uncertain result; do not submit a new key.", + "schema": { + "type": "string", + "format": "uuid" + } + }, + "X-Polymorfa-Transport": { + "description": "Transport selected by Hybrid routing, when a route was admitted.", + "schema": { + "type": "string", + "enum": [ + "linked_devices", + "official_api" + ] + } + }, + "X-Polymorfa-Routing-Reason": { + "description": "Reason for the selected Hybrid route, when a route was admitted.", + "schema": { + "type": "string" + } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "503": { + "description": "Session or service unavailable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + } + } + }, + "headers": { + "X-Polymorfa-Operation-Id": { + "description": "Hybrid operation ID when a send is reserved. Read its status after an uncertain result; do not submit a new key.", + "schema": { + "type": "string", + "format": "uuid" + } + }, + "X-Polymorfa-Transport": { + "description": "Transport selected by Hybrid routing, when a route was admitted.", + "schema": { + "type": "string", + "enum": [ + "linked_devices", + "official_api" + ] + } + }, + "X-Polymorfa-Routing-Reason": { + "description": "Reason for the selected Hybrid route, when a route was admitted.", + "schema": { + "type": "string" + } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -20308,6 +22488,15 @@ "schema": { "type": "string" } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } } @@ -20364,6 +22553,17 @@ "$ref": "#/components/schemas/MessageActionResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "202": { @@ -20374,6 +22574,17 @@ "$ref": "#/components/schemas/AsyncAcceptedResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -20384,6 +22595,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -20394,6 +22616,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "402": { @@ -20404,6 +22637,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -20414,6 +22658,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -20424,6 +22679,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { @@ -20434,6 +22700,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -20444,6 +22721,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "502": { @@ -20454,6 +22742,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { @@ -20464,6 +22763,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "504": { @@ -20474,6 +22784,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -20528,6 +22849,17 @@ "$ref": "#/components/schemas/MessageOperationResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -20538,6 +22870,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -20548,6 +22891,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -20558,6 +22912,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -20568,6 +22933,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -20578,6 +22954,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { @@ -20588,6 +22975,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -20693,6 +23091,15 @@ "schema": { "type": "string" } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -20715,6 +23122,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -20736,6 +23154,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -20757,6 +23186,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -20778,6 +23218,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { @@ -20821,6 +23272,15 @@ "schema": { "type": "string" } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } }, "content": { @@ -20884,6 +23344,15 @@ "schema": { "type": "string" } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } } @@ -20992,6 +23461,15 @@ "schema": { "type": "string" } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -21014,6 +23492,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -21035,6 +23524,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -21056,6 +23556,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -21077,6 +23588,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { @@ -21120,6 +23642,15 @@ "schema": { "type": "string" } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } }, "content": { @@ -21183,6 +23714,15 @@ "schema": { "type": "string" } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } } @@ -21249,6 +23789,15 @@ "schema": { "type": "string" } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } }, "content": { @@ -21267,6 +23816,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -21277,6 +23837,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -21287,6 +23858,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -21297,6 +23879,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { @@ -21307,6 +23900,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -21358,6 +23962,17 @@ "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -21379,6 +23994,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -21400,6 +24026,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -21421,6 +24058,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -21442,6 +24090,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -21463,6 +24122,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -21515,6 +24185,17 @@ "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -21536,6 +24217,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -21557,6 +24249,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -21578,6 +24281,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -21599,6 +24313,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -21620,6 +24345,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -21681,6 +24417,17 @@ "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -21702,6 +24449,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -21723,6 +24481,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -21744,6 +24513,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -21765,6 +24545,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -21786,6 +24577,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -21893,6 +24695,15 @@ "schema": { "type": "string" } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } }, "content": { @@ -21911,6 +24722,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -21921,6 +24743,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -21931,6 +24764,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -21941,6 +24785,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { @@ -21951,6 +24806,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -22006,6 +24872,15 @@ "schema": { "type": "string" } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } }, "content": { @@ -22024,6 +24899,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -22034,6 +24920,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -22044,6 +24941,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -22054,6 +24962,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { @@ -22064,6 +24983,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -22202,6 +25132,15 @@ "schema": { "type": "string" } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } }, "content": { @@ -22220,6 +25159,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -22230,6 +25180,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -22240,6 +25201,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -22250,6 +25222,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { @@ -22260,6 +25243,17 @@ "$ref": "#/components/schemas/PublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -22271,23 +25265,30 @@ ] } }, - "/messaging/{session}/calls/{callId}/reject": { - "post": { - "operationId": "rejectCall", + "/messaging/{session}/chats/{conversation}/messages/{messageId}/media": { + "get": { + "operationId": "getChatMessageMedia", "tags": [ - "Calls" + "Chats" ], - "summary": "Reject an incoming WhatsApp call", + "summary": "Download stored message media", + "description": "**Protocols:** Meta Cloud API\n\n**Required scopes (all):** `messages:read` · `media:read`\n\nReturns the stored copy of an Official API message's media when `mediaRetrieval.state` is `stored`. Requires `messages:read` and `media:read`. Beta: requires team enrollment.", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "chats:manage", + "x-required-scopes": [ + "messages:read", + "media:read" + ], + "x-required-scope-mode": "all", "parameters": [ { "schema": { - "type": "string" + "type": "string", + "minLength": 1, + "description": "Number ID." }, "required": true, "name": "session", @@ -22296,167 +25297,123 @@ { "schema": { "type": "string", - "minLength": 1, - "maxLength": 128 + "description": "Conversation ID or E.164 phone number." }, "required": true, - "name": "callId", + "name": "conversation", + "in": "path" + }, + { + "schema": { + "type": "string", + "pattern": "^[1-9][0-9]{0,18}$", + "description": "Opaque Polymorfa resource ID. Keep this value as a string.", + "example": "739182640518203" + }, + "required": true, + "name": "messageId", "in": "path" } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RejectCallRequest" - } - } - } - }, "responses": { "200": { - "description": "Call rejected", + "description": "The media bytes, with the stored content type", + "headers": { + "Polymorfa-Data-Region": { + "description": "Data region that served the stored history.", + "schema": { + "type": "string" + } + } + }, "content": { - "application/json": { + "*/*": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "type": "string", + "format": "binary" } } } }, "400": { - "description": "Bad request — request body or params failed validation", + "description": "A filter or cursor is invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "invalid_request_error", - "code": "invalid_parameter", - "message": "Correct the invalid request field.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } } }, "401": { - "description": "Missing or invalid authentication token", + "description": "The credential is missing or invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "authentication_error", - "code": "invalid_credential", - "message": "Supply a valid credential.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } } }, "403": { - "description": "Authenticated but not permitted (missing scope or rule violation)", + "description": "The credential lacks the scope, is a client token, or the team is not enrolled in the beta.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "permission_error", - "code": "permission_denied", - "message": "The credential does not have access to this resource.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } } }, "404": { - "description": "Resource not found", + "description": "The Number, conversation or message is unavailable, no stored copy exists, or hosted message storage is not enabled (`hms_not_enabled`).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "invalid_request_error", - "code": "resource_not_found", - "message": "The resource is unavailable to this caller.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } } }, - "500": { - "description": "Internal server error", + "503": { + "description": "The history service for the Number's data region is unavailable.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "api_error", - "code": "internal_error", - "message": "An unexpected service failure occurred. Keep the request ID for support.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } } } }, "x-mint": { - "content": "Linked Device API chats:manage" + "content": "Meta Cloud API messages:read media:read" }, "x-protocols": [ - "Linked Device API" - ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `chats:manage`" + "Meta Cloud API" + ] } }, - "/messaging/{session}/call-permissions/{to}": { + "/messaging/{session}/chats/{conversation}/service-window": { "get": { - "operationId": "getCallPermission", + "operationId": "getCustomerServiceWindow", "tags": [ - "Calls" + "Chats" ], - "summary": "Get a person's call permission", - "description": "**Protocols:** Meta Cloud API\n\n**Required scope:** `sessions:read`\n\nReturns whether the person has allowed this number to call them. On Cloud API numbers WhatsApp is asked on every request and the answer includes its limits on permission requests and calls. Linked-device numbers return `409 unsupported_for_connection`.", + "summary": "Get the customer service window", + "description": "**Protocols:** Meta Cloud API\n\n**Required scope:** `chats:read`\n\nReturns whether free-form messages can be sent to a person from a Number's active Official API connection. WhatsApp's customer service window opens for 24 hours when the person messages or calls the Number. Outside it, only approved templates can be sent. Beta: requires team enrollment.", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "sessions:read", + "x-required-scope": "chats:read", "parameters": [ { "schema": { - "type": "string" + "type": "string", + "minLength": 1, + "description": "Number ID." }, "required": true, "name": "session", @@ -22465,372 +25422,281 @@ { "schema": { "type": "string", - "description": "User ID or phone number in E.164 format (URL-encode `+` as `%2B`)." + "description": "Conversation ID or E.164 phone number." }, "required": true, - "name": "to", + "name": "conversation", "in": "path" } ], "responses": { "200": { - "description": "The call permission.", + "description": "The window state", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetCallPermissionResponse" + "$ref": "#/components/schemas/GetCustomerServiceWindowResponse" } } } }, "400": { - "description": "Bad request — request body or params failed validation", + "description": "A parameter is invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "invalid_request_error", - "code": "invalid_parameter", - "message": "Correct the invalid request field.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } } }, "401": { - "description": "Missing or invalid authentication token", + "description": "The credential is missing or invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "authentication_error", - "code": "invalid_credential", - "message": "Supply a valid credential.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } } }, "403": { - "description": "Authenticated but not permitted (missing scope or rule violation)", + "description": "The credential lacks the scope, is a client token, or the team is not enrolled in the beta.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "permission_error", - "code": "permission_denied", - "message": "The credential does not have access to this resource.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } } }, "404": { - "description": "Resource not found", + "description": "The Number or conversation is unavailable.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "invalid_request_error", - "code": "resource_not_found", - "message": "The resource is unavailable to this caller.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } } }, "409": { - "description": "`unsupported_for_connection`: the number is not a Cloud API number.", + "description": "The Number has no Official API connection (`unsupported_for_connection`).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "conflict_error", - "code": "unsupported_for_connection", - "message": "The session's connection does not support this call operation.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#unsupported-for-connection" } } } }, - "500": { - "description": "Internal server error", + "503": { + "description": "The Number's regional evidence service is temporarily unavailable.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "api_error", - "code": "internal_error", - "message": "An unexpected service failure occurred. Keep the request ID for support.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } } } }, "x-mint": { - "content": "Meta Cloud API sessions:read" + "content": "Meta Cloud API chats:read" }, "x-protocols": [ "Meta Cloud API" ] } }, - "/messaging/voip/calls/check": { - "post": { - "operationId": "checkCall", + "/messaging/{session}/meta-pricing": { + "get": { + "operationId": "getMetaPricingSummary", "tags": [ - "Calls" + "Sessions" ], - "summary": "Check whether a call can be placed", - "description": "**Required scope:** `sessions:read`\n\nRuns the checks a call placement runs, without calling: the number's calling switch, the team's do-not-call list and blocked country codes, and on Cloud API numbers the person's call permission and WhatsApp's call limits. If WhatsApp's permission status is unavailable and no other refusal is known, returns 503 service_unavailable. Server credentials only.", + "summary": "Summarize Meta-reported pricing", + "description": "**Protocols:** Meta Cloud API\n\n**Required scope:** `sessions:read`\n\nCounts the messages sent from the Number's active Official API connection generation by the pricing classification WhatsApp reported for each one. Meta bills your WhatsApp Business Account directly. These counts are not a Polymorfa charge and contain no prices. Each message is counted in the period of its first pricing report. Beta: requires team enrollment.", "security": [ { "BearerAuth": [] } ], "x-required-scope": "sessions:read", - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CallCheckRequest" - } - } + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1, + "description": "Number ID." + }, + "required": true, + "name": "session", + "in": "path" + }, + { + "schema": { + "type": "string", + "format": "date-time", + "description": "Start of the period, inclusive (ISO 8601). Defaults to 30 days before `until`." + }, + "required": false, + "name": "since", + "in": "query" + }, + { + "schema": { + "type": "string", + "format": "date-time", + "description": "End of the period, exclusive (ISO 8601). Defaults to now. The period can be at most 93 days." + }, + "required": false, + "name": "until", + "in": "query" } - }, + ], "responses": { "200": { - "description": "Check result.", + "description": "Meta-reported message counts", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CheckCallResponse" + "$ref": "#/components/schemas/GetMetaPricingSummaryResponse" } } } }, "400": { - "description": "Bad request — request body or params failed validation", + "description": "A parameter is invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "invalid_request_error", - "code": "invalid_parameter", - "message": "Correct the invalid request field.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } } }, "401": { - "description": "Missing or invalid authentication token", + "description": "The credential is missing or invalid.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "authentication_error", - "code": "invalid_credential", - "message": "Supply a valid credential.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } } }, "403": { - "description": "Authenticated but not permitted (missing scope or rule violation)", + "description": "The credential lacks the scope, is a client token, or the team is not enrolled in the beta.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "permission_error", - "code": "permission_denied", - "message": "The credential does not have access to this resource.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } } }, "404": { - "description": "Resource not found", + "description": "The Number is unavailable.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "invalid_request_error", - "code": "resource_not_found", - "message": "The resource is unavailable to this caller.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } } }, - "500": { - "description": "Internal server error", + "409": { + "description": "The Number has no Official API connection (`unsupported_for_connection`).", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "api_error", - "code": "internal_error", - "message": "An unexpected service failure occurred. Keep the request ID for support.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } } }, "503": { - "description": "`service_unavailable`: required call-check state is unavailable; retry later.", + "description": "The Number's regional evidence service is temporarily unavailable.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "api_error", - "code": "service_unavailable", - "message": "A required service is unavailable. Check completion before repeating a write.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" } } } } }, "x-mint": { - "content": "sessions:read" - } + "content": "Meta Cloud API sessions:read" + }, + "x-protocols": [ + "Meta Cloud API" + ] } }, - "/messaging/voip/calls/{id}/offer": { + "/messaging/{session}/calls/{callId}/reject": { "post": { - "operationId": "voipOffer", - "x-internal": true, + "operationId": "rejectCall", "tags": [ - "VoIP" + "Calls" ], - "summary": "Attach a WebRTC connection and receive the pod's answer + ICE servers", - "description": "**Required scope:** `sessions:manage`\n\nSubmits the SDP offer for one connection. The offer carries one audio transceiver, a data channel negotiated out of band with label `pmfa.calls` and id 0, and one or more video transceivers. Reusing a `connectionId` replaces that connection.", + "summary": "Reject an incoming WhatsApp call", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "sessions:manage", + "x-required-scope": "chats:manage", "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "session", + "in": "path" + }, { "schema": { "type": "string", "minLength": 1, - "example": "call-123" + "maxLength": 128 }, "required": true, - "name": "id", + "name": "callId", "in": "path" } ], "requestBody": { - "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/VoipOfferRequest" + "$ref": "#/components/schemas/RejectCallRequest" } } } }, "responses": { "200": { - "description": "SDP answer with STUN/TURN ICE servers", + "description": "Call rejected", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/VoipOfferResponse" + "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -22852,6 +25718,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -22873,6 +25750,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -22894,6 +25782,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -22915,68 +25814,16 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } - } - }, - "409": { - "description": "`call_claimed` when another participant claimed the call, `connection_limit` when the call has no free connection, or `state_conflict` when the call is not ready", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "conflict_error", - "code": "call_claimed", - "message": "Another participant claimed this call.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#call-claimed" - } - } - } - }, - "410": { - "description": "Call ended — the pod that owned its media is gone (pod_lost)", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "invalid_request_error", - "code": "resource_gone", - "message": "The requested retained resource has expired or was removed.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#resource-gone" - } - } - } - }, - "429": { - "description": "Call setup rate exceeded for this client token, or its concurrency limit is reached", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "rate_limit_error", - "code": "rate_limit_exceeded", - "message": "The request limit was reached. Follow Retry-After when supplied.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#rate-limit-exceeded" - } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -22999,81 +25846,82 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } - } - }, - "503": { - "description": "Call unavailable — the owning pod is at capacity or draining, or call authority cannot be verified", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "api_error", - "code": "service_unavailable", - "message": "A required service is unavailable. Check completion before repeating a write.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" - } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } } }, "x-mint": { - "content": "sessions:manage" - } + "content": "Linked Device API chats:manage" + }, + "x-protocols": [ + "Linked Device API" + ], + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `chats:manage`" } }, - "/messaging/voip/calls/{id}/renegotiate": { - "post": { - "operationId": "voipRenegotiate", - "x-internal": true, + "/messaging/{session}/call-permissions/{to}": { + "get": { + "operationId": "getCallPermission", "tags": [ - "VoIP" + "Calls" ], - "summary": "Re-offer on an established connection and receive the pod's answer", - "description": "**Required scope:** `sessions:manage`\n\nRenegotiates one connection, for example to add video, add receive transceivers after `video_slots_exhausted`, or restart ICE.", + "summary": "Get a person's call permission", + "description": "**Protocols:** Meta Cloud API\n\n**Required scope:** `sessions:read`\n\nReturns whether the person has allowed this number to call them. On Cloud API numbers WhatsApp is asked on every request and the answer includes its limits on permission requests and calls. Linked-device numbers return `409 unsupported_for_connection`.", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "sessions:manage", + "x-required-scope": "sessions:read", "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "session", + "in": "path" + }, { "schema": { "type": "string", - "minLength": 1, - "example": "call-123" + "description": "User ID or phone number in E.164 format (URL-encode `+` as `%2B`)." }, "required": true, - "name": "id", + "name": "to", "in": "path" } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/VoipRenegotiateRequest" - } - } - } - }, "responses": { "200": { - "description": "SDP answer for the re-offer, on the same peer connection", + "description": "The call permission.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/VoipRenegotiateResponse" + "$ref": "#/components/schemas/GetCallPermissionResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -23095,6 +25943,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -23116,6 +25975,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -23137,6 +26007,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -23158,10 +26039,21 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { - "description": "`call_claimed` when another participant claimed the call, `connection_limit` when the call has no free connection, or `state_conflict` when the call is not ready", + "description": "`unsupported_for_connection`: the number is not a Cloud API number.", "content": { "application/json": { "schema": { @@ -23170,35 +26062,25 @@ "example": { "error": { "type": "conflict_error", - "code": "call_claimed", - "message": "Another participant claimed this call.", + "code": "unsupported_for_connection", + "message": "The session's connection does not support this call operation.", "param": null, "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" }, "data": null, - "docs": "https://docs.polymorfa.com/api/errors#call-claimed" + "docs": "https://docs.polymorfa.com/api/errors#unsupported-for-connection" } } - } - }, - "410": { - "description": "Call ended — the pod that owned its media is gone (pod_lost)", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "invalid_request_error", - "code": "resource_gone", - "message": "The requested retained resource has expired or was removed.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#resource-gone" - } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -23221,68 +26103,72 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } - } - }, - "503": { - "description": "Call unavailable — the owning pod is at capacity or draining, or call authority cannot be verified", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "api_error", - "code": "service_unavailable", - "message": "A required service is unavailable. Check completion before repeating a write.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" - } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } } }, "x-mint": { - "content": "sessions:manage" - } + "content": "Meta Cloud API sessions:read" + }, + "x-protocols": [ + "Meta Cloud API" + ] } }, - "/messaging/voip/calls": { + "/messaging/voip/calls/check": { "post": { - "operationId": "voipPlaceCall", + "operationId": "checkCall", "tags": [ - "VoIP" + "Calls" ], - "summary": "Place an outbound call from a session", - "description": "**Required scope:** `sessions:manage`\n\nOriginates a WhatsApp call to `to` (an E.164 phone number or a user ID) from the named session. The call is owned by the placing participant and rings on the callee's device: `call.accepted` follows when they pick up and `call.ended` when either side hangs up. Carry its audio and video with a Polymorfa Calls SDK. `exclusive: true` claims the call for the placing participant. Client tokens place from their bound session; server credentials must name one. Outbound starts share a rate limit per organization and source session across server credentials and client tokens; client-token rules also apply.", + "summary": "Check whether a call can be placed", + "description": "**Required scope:** `sessions:read`\n\nRuns the checks a call placement runs, without calling: the number's calling switch, the team's do-not-call list and blocked country codes, and on Cloud API numbers the person's call permission and WhatsApp's call limits. If WhatsApp's permission status is unavailable and no other refusal is known, returns 503 service_unavailable. Server credentials only.", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "sessions:manage", + "x-required-scope": "sessions:read", "requestBody": { "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/VoipPlaceCallRequest" + "$ref": "#/components/schemas/CallCheckRequest" } } } }, "responses": { - "201": { - "description": "Call placed and ringing", + "200": { + "description": "Check result.", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/VoipPlacedCallResponse" + "$ref": "#/components/schemas/CheckCallResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -23304,6 +26190,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -23325,31 +26222,21 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } - } - }, - "402": { - "description": "`gate_limit_reached` when an enforced usage limit refuses placement; `entitlement_required` when the account lacks a required capability. The outbound monthly gate remains in record mode until placement reservations are available.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "permission_error", - "code": "gate_limit_reached", - "message": "A usage limit for this team or number was reached.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#gate-limit-reached" - } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, "403": { - "description": "`call_permission_required` when the recipient has not granted this business permission to call them, `number_restricted` when WhatsApp restricts the number to calling people it has already chatted with and the callee is not one of them (`Retry-After` gives the seconds until the restriction ends, when known), or the credential is not permitted", + "description": "Authenticated but not permitted (missing scope or rule violation)", "content": { "application/json": { "schema": { @@ -23358,15 +26245,26 @@ "example": { "error": { "type": "permission_error", - "code": "call_permission_required", - "message": "The recipient has not granted this business permission to call them.", + "code": "permission_denied", + "message": "The credential does not have access to this resource.", "param": null, "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" }, "data": null, - "docs": "https://docs.polymorfa.com/api/errors#call-permission-required" + "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -23388,68 +26286,16 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } - } - }, - "409": { - "description": "`unsupported_for_connection` when the session's connection cannot place this call (Cloud API calls are audio only), or `state_conflict` when the named session cannot place an outbound call yet", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "conflict_error", - "code": "unsupported_for_connection", - "message": "The session's connection does not support this call operation.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#unsupported-for-connection" - } - } - } - }, - "410": { - "description": "Call ended — the pod that owned its media is gone (pod_lost)", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "invalid_request_error", - "code": "resource_gone", - "message": "The requested retained resource has expired or was removed.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#resource-gone" - } - } - } - }, - "429": { - "description": "Outbound call rate exceeded for this organization and source session, or a client-token setup or concurrency limit was reached", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "rate_limit_error", - "code": "rate_limit_exceeded", - "message": "The request limit was reached. Follow Retry-After when supplied.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#rate-limit-exceeded" - } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -23472,52 +26318,21 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } - } - }, - "501": { - "description": "Outbound calls are not available on this deployment", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "invalid_request_error", - "code": "operation_not_supported", - "message": "This operation is not supported.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#operation-not-supported" - } - } - } - }, - "502": { - "description": "`upstream_failure` when the Cloud API refused or failed the call action", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "upstream_error", - "code": "upstream_failure", - "message": "The messaging provider could not complete the request.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#upstream-failure" - } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, "503": { - "description": "Call unavailable — the owning pod is at capacity or draining, call authority cannot be verified, or usage limits could not be checked", + "description": "`service_unavailable`: required call-check state is unavailable; retry later.", "content": { "application/json": { "schema": { @@ -23535,22 +26350,34 @@ "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "sessions:manage" + "content": "sessions:read" } } }, - "/messaging/voip/calls/{id}/accept": { + "/messaging/voip/calls/{id}/offer": { "post": { - "operationId": "voipAcceptCall", + "operationId": "voipOffer", + "x-internal": true, "tags": [ "VoIP" ], - "summary": "Answer or join a call", - "description": "**Required scope:** `sessions:manage`\n\nThe first accept answers a ringing call. `exclusive: true` claims it: other participants then receive `409 call_claimed` for accept and media, and their open connections close. Without a claim, later accepts join the answered call. Retrying with the same participant returns the same result. An exclusive request from another participant on a shared call is refused with `409 call_claimed`.", + "summary": "Attach a WebRTC connection and receive the pod's answer + ICE servers", + "description": "**Required scope:** `sessions:manage`\n\nSubmits the SDP offer for one connection. The offer carries one audio transceiver, a data channel negotiated out of band with label `pmfa.calls` and id 0, and one or more video transceivers. Reusing a `connectionId` replaces that connection.", "security": [ { "BearerAuth": [] @@ -23570,23 +26397,35 @@ } ], "requestBody": { + "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/VoipAcceptCallRequest" + "$ref": "#/components/schemas/VoipOfferRequest" } } } }, "responses": { "200": { - "description": "Call answered or joined", + "description": "SDP answer with STUN/TURN ICE servers", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/VoipAcceptCallResponse" + "$ref": "#/components/schemas/VoipOfferResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -23608,6 +26447,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -23629,6 +26479,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -23650,6 +26511,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -23671,10 +26543,21 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { - "description": "`call_claimed` when another participant claimed the call, or `state_conflict` when the call is not ready", + "description": "`call_claimed` when another participant claimed the call, `connection_limit` when the call has no free connection, or `state_conflict` when the call is not ready", "content": { "application/json": { "schema": { @@ -23692,6 +26575,17 @@ "docs": "https://docs.polymorfa.com/api/errors#call-claimed" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "410": { @@ -23713,6 +26607,49 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-gone" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "429": { + "description": "Call setup rate exceeded for this client token, or its concurrency limit is reached", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "rate_limit_error", + "code": "rate_limit_exceeded", + "message": "The request limit was reached. Follow Retry-After when supplied.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#rate-limit-exceeded" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -23734,6 +26671,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { @@ -23755,6 +26703,17 @@ "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -23763,14 +26722,15 @@ } } }, - "/messaging/voip/calls/{id}/reject": { + "/messaging/voip/calls/{id}/renegotiate": { "post": { - "operationId": "voipRejectCall", + "operationId": "voipRenegotiate", + "x-internal": true, "tags": [ "VoIP" ], - "summary": "Decline a ringing call", - "description": "**Required scope:** `sessions:manage`\n\nDeclines a ringing call and releases it. Returns `409 call_not_ringing` when the call is not ringing.", + "summary": "Re-offer on an established connection and receive the pod's answer", + "description": "**Required scope:** `sessions:manage`\n\nRenegotiates one connection, for example to add video, add receive transceivers after `video_slots_exhausted`, or restart ICE.", "security": [ { "BearerAuth": [] @@ -23790,23 +26750,35 @@ } ], "requestBody": { + "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/VoipRejectCallRequest" + "$ref": "#/components/schemas/VoipRenegotiateRequest" } } } }, "responses": { - "202": { - "description": "Call declined", + "200": { + "description": "SDP answer for the re-offer, on the same peer connection", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/VoipRenegotiateResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -23828,6 +26800,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -23849,6 +26832,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -23870,6 +26864,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -23891,10 +26896,21 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { - "description": "`call_not_ringing` when the call was already answered, declined, or ended", + "description": "`call_claimed` when another participant claimed the call, `connection_limit` when the call has no free connection, or `state_conflict` when the call is not ready", "content": { "application/json": { "schema": { @@ -23903,15 +26919,26 @@ "example": { "error": { "type": "conflict_error", - "code": "call_not_ringing", - "message": "The call is no longer ringing.", + "code": "call_claimed", + "message": "Another participant claimed this call.", "param": null, "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" }, "data": null, - "docs": "https://docs.polymorfa.com/api/errors#call-not-ringing" + "docs": "https://docs.polymorfa.com/api/errors#call-claimed" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "410": { @@ -23933,6 +26960,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-gone" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -23954,6 +26992,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { @@ -23975,6 +27024,17 @@ "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -23983,51 +27043,50 @@ } } }, - "/messaging/voip/calls/{id}/leave": { + "/messaging/voip/calls": { "post": { - "operationId": "voipLeaveCall", + "operationId": "voipPlaceCall", "tags": [ "VoIP" ], - "summary": "Close one connection without ending the call", - "description": "**Required scope:** `sessions:manage`\n\nDisconnects the named connection. The call and the participant's other connections continue. Use `DELETE /voip/calls/{id}` to end the call for everyone.", + "summary": "Place an outbound call from a session", + "description": "**Required scope:** `sessions:manage`\n\nOriginates a WhatsApp call to `to`, an ad-hoc group call to `participants` (2 to 31 distinct E.164 phone numbers or public user IDs), or every remote member of `groupId` (a public group ID). Group rosters must contain 2 to 31 remote members; a membership change during placement refuses the call. Provide exactly one destination form. Group calls require a Linked Device number and check every recipient against the team policy and number restrictions before dialing. The call is owned by the placing participant and rings on the callee's device: `call.accepted` follows when they pick up and `call.ended` when either side hangs up. Carry its audio and video with a Polymorfa Calls SDK. `exclusive: true` claims the call for the placing participant. Client tokens place from their bound session; server credentials must name one. Outbound starts share a rate limit per organization and source session across server credentials and client tokens; client-token rules also apply.", "security": [ { "BearerAuth": [] } ], "x-required-scope": "sessions:manage", - "parameters": [ - { - "schema": { - "type": "string", - "minLength": 1, - "example": "call-123" - }, - "required": true, - "name": "id", - "in": "path" - } - ], "requestBody": { "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/VoipLeaveRequest" + "$ref": "#/components/schemas/VoipPlaceCallRequest" } } } }, "responses": { - "202": { - "description": "Connection closed", + "201": { + "description": "Call placed and ringing", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/VoipPlacedCallResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -24049,6 +27108,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -24070,10 +27140,53 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "402": { + "description": "`gate_limit_reached` when an enforced usage limit refuses placement; `entitlement_required` when the account lacks a required capability. The outbound monthly gate remains in record mode until placement reservations are available.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "permission_error", + "code": "gate_limit_reached", + "message": "A usage limit for this team or number was reached.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#gate-limit-reached" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { - "description": "Authenticated but not permitted (missing scope or rule violation)", + "description": "`call_permission_required` when the recipient has not granted this business permission to call them, `number_restricted` when WhatsApp restricts the number to calling people it has already chatted with and the callee is not one of them (`Retry-After` gives the seconds until the restriction ends, when known), or the credential is not permitted", "content": { "application/json": { "schema": { @@ -24082,15 +27195,26 @@ "example": { "error": { "type": "permission_error", - "code": "permission_denied", - "message": "The credential does not have access to this resource.", + "code": "call_permission_required", + "message": "The recipient has not granted this business permission to call them.", "param": null, "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" }, "data": null, - "docs": "https://docs.polymorfa.com/api/errors#permission-denied" + "docs": "https://docs.polymorfa.com/api/errors#call-permission-required" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -24112,10 +27236,21 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { - "description": "Call not ready — no media session on the owning pod yet", + "description": "`unsupported_for_connection` when the session's connection cannot place this call (Cloud API calls are audio only), or `state_conflict` when the named session cannot place an outbound call yet", "content": { "application/json": { "schema": { @@ -24124,15 +27259,26 @@ "example": { "error": { "type": "conflict_error", - "code": "state_conflict", - "message": "The request conflicts with the resource state.", + "code": "unsupported_for_connection", + "message": "The session's connection does not support this call operation.", "param": null, "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" }, "data": null, - "docs": "https://docs.polymorfa.com/api/errors#state-conflict" + "docs": "https://docs.polymorfa.com/api/errors#unsupported-for-connection" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "410": { @@ -24154,6 +27300,49 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-gone" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "429": { + "description": "Outbound call rate exceeded for this organization and source session, or a client-token setup or concurrency limit was reached", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "rate_limit_error", + "code": "rate_limit_exceeded", + "message": "The request limit was reached. Follow Retry-After when supplied.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#rate-limit-exceeded" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -24175,10 +27364,85 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "501": { + "description": "Outbound calls are not available on this deployment", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "operation_not_supported", + "message": "This operation is not supported.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#operation-not-supported" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "502": { + "description": "`upstream_failure` when the Cloud API refused or failed the call action", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "upstream_error", + "code": "upstream_failure", + "message": "The messaging provider could not complete the request.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#upstream-failure" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { - "description": "Call unavailable — the owning pod is at capacity or draining, or call authority cannot be verified", + "description": "Call unavailable — the owning pod is at capacity or draining, call authority cannot be verified, or usage limits could not be checked", "content": { "application/json": { "schema": { @@ -24196,6 +27460,17 @@ "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -24204,14 +27479,14 @@ } } }, - "/messaging/voip/calls/{id}/participants": { + "/messaging/voip/calls/{id}/accept": { "post": { - "operationId": "voipAddParticipant", + "operationId": "voipAcceptCall", "tags": [ "VoIP" ], - "summary": "Invite another party into a live call", - "description": "**Required scope:** `sessions:manage`\n\nRings `to` (an E.164 phone number or a user ID) into the call, turning a 1:1 call into a group call. The invitee enters the roster as `invited`; roster changes reach media sockets as `participant_joined` / `participant_state` / `participant_left` frames. 409 until the call has a live leg.", + "summary": "Answer or join a call", + "description": "**Required scope:** `sessions:manage`\n\nThe first accept answers a ringing call. `exclusive: true` claims it: other participants then receive `409 call_claimed` for accept and media, and their open connections close. Without a claim, later accepts join the answered call. Retrying with the same participant returns the same result. An exclusive request from another participant on a shared call is refused with `409 call_claimed`.", "security": [ { "BearerAuth": [] @@ -24234,20 +27509,31 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/VoipAddParticipantRequest" + "$ref": "#/components/schemas/VoipAcceptCallRequest" } } } }, "responses": { - "201": { - "description": "Participant invited", + "200": { + "description": "Call answered or joined", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/VoipAddParticipantResponse" + "$ref": "#/components/schemas/VoipAcceptCallResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -24269,6 +27555,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -24290,10 +27587,21 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { - "description": "`number_restricted` when WhatsApp restricts the number to calling people it has already chatted with and the invitee is not one of them (`Retry-After` gives the seconds until the restriction ends, when known), or the credential is not permitted", + "description": "Authenticated but not permitted (missing scope or rule violation)", "content": { "application/json": { "schema": { @@ -24302,15 +27610,26 @@ "example": { "error": { "type": "permission_error", - "code": "number_restricted", - "message": "WhatsApp restricts this number to calling people it has already chatted with. The restriction lifts on its own; follow Retry-After when supplied.", + "code": "permission_denied", + "message": "The credential does not have access to this resource.", "param": null, "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" }, "data": null, - "docs": "https://docs.polymorfa.com/api/errors#number-restricted" + "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -24332,10 +27651,21 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { - "description": "`unsupported_for_connection` when the session connects through the Cloud API, which has no group calls, or `state_conflict` when the call has no live leg yet", + "description": "`call_claimed` when another participant claimed the call, or `state_conflict` when the call is not ready", "content": { "application/json": { "schema": { @@ -24344,15 +27674,26 @@ "example": { "error": { "type": "conflict_error", - "code": "unsupported_for_connection", - "message": "The session's connection does not support this call operation.", + "code": "call_claimed", + "message": "Another participant claimed this call.", "param": null, "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" }, "data": null, - "docs": "https://docs.polymorfa.com/api/errors#unsupported-for-connection" + "docs": "https://docs.polymorfa.com/api/errors#call-claimed" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "410": { @@ -24374,26 +27715,16 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-gone" } } - } - }, - "429": { - "description": "Call setup rate exceeded for this client token, or its concurrency limit is reached", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "rate_limit_error", - "code": "rate_limit_exceeded", - "message": "The request limit was reached. Follow Retry-After when supplied.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#rate-limit-exceeded" - } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -24416,6 +27747,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { @@ -24437,6 +27779,17 @@ "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -24445,14 +27798,14 @@ } } }, - "/messaging/voip/calls/{id}/candidate": { + "/messaging/voip/calls/{id}/reject": { "post": { - "operationId": "voipCandidate", - "x-internal": true, + "operationId": "voipRejectCall", "tags": [ "VoIP" ], - "summary": "Trickle a local ICE candidate to one connection", + "summary": "Decline a ringing call", + "description": "**Required scope:** `sessions:manage`\n\nDeclines a ringing call and releases it. Returns `409 call_not_ringing` when the call is not ringing.", "security": [ { "BearerAuth": [] @@ -24472,24 +27825,34 @@ } ], "requestBody": { - "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/VoipCandidateRequest" + "$ref": "#/components/schemas/VoipRejectCallRequest" } } } }, "responses": { "202": { - "description": "Candidate accepted", + "description": "Call declined", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -24511,6 +27874,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -24532,6 +27906,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -24553,6 +27938,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -24574,10 +27970,21 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { - "description": "Call not ready — no media session on the owning pod yet", + "description": "`call_not_ringing` when the call was already answered, declined, or ended", "content": { "application/json": { "schema": { @@ -24586,15 +27993,26 @@ "example": { "error": { "type": "conflict_error", - "code": "state_conflict", - "message": "The request conflicts with the resource state.", + "code": "call_not_ringing", + "message": "The call is no longer ringing.", "param": null, "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" }, "data": null, - "docs": "https://docs.polymorfa.com/api/errors#state-conflict" + "docs": "https://docs.polymorfa.com/api/errors#call-not-ringing" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "410": { @@ -24616,6 +28034,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-gone" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -24637,6 +28066,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { @@ -24658,23 +28098,33 @@ "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { "content": "sessions:manage" - }, - "description": "**Required scope:** `sessions:manage`" + } } }, - "/messaging/voip/calls/{id}/candidates": { - "get": { - "operationId": "voipCandidates", - "x-internal": true, + "/messaging/voip/calls/{id}/leave": { + "post": { + "operationId": "voipLeaveCall", "tags": [ "VoIP" ], - "summary": "Poll remote ICE candidates queued for the browser", + "summary": "Close one connection without ending the call", + "description": "**Required scope:** `sessions:manage`\n\nDisconnects the named connection. The call and the participant's other connections continue. Use `DELETE /voip/calls/{id}` to end the call for everyone.", "security": [ { "BearerAuth": [] @@ -24693,15 +28143,36 @@ "in": "path" } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VoipLeaveRequest" + } + } + } + }, "responses": { - "200": { - "description": "Queued remote candidates (drained on read)", + "202": { + "description": "Connection closed", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/VoipCandidatesEnvelope" + "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -24723,6 +28194,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -24744,6 +28226,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -24765,6 +28258,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -24786,6 +28290,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { @@ -24807,6 +28322,17 @@ "docs": "https://docs.polymorfa.com/api/errors#state-conflict" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "410": { @@ -24828,6 +28354,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-gone" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -24849,6 +28386,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { @@ -24870,23 +28418,33 @@ "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { "content": "sessions:manage" - }, - "description": "**Required scope:** `sessions:manage`" + } } }, - "/messaging/voip/calls/{id}/reports": { + "/messaging/voip/calls/{id}/participants": { "post": { - "operationId": "voipReportCallDiagnostics", + "operationId": "voipAddParticipant", "tags": [ "VoIP" ], - "summary": "Report call diagnostics from your app", - "description": "**Required scope:** `sessions:manage`\n\nRecords quality figures or an error that your app measured for one of its connections. The Console shows the latest quality report and the reported errors of each connection on the call. Reports are optional and are accepted while the call is live and for 10 minutes after it ends. Each connection can send one quality report every 5 seconds and 20 error reports per minute. At most 200 reports are kept per call: at that limit a quality report replaces the same connection's oldest quality report. A quality report from a connection with no stored quality report, and every error report, is accepted but not stored.", + "summary": "Invite another party into a live call", + "description": "**Required scope:** `sessions:manage`\n\nRings `to` (an E.164 phone number or a user ID) into the call, turning a 1:1 call into a group call. The invitee enters the roster as `invited`; roster changes reach media sockets as `participant_joined` / `participant_state` / `participant_left` frames. 409 until the call has a live leg.", "security": [ { "BearerAuth": [] @@ -24906,24 +28464,34 @@ } ], "requestBody": { - "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/VoipCallReportRequest" + "$ref": "#/components/schemas/VoipAddParticipantRequest" } } } }, "responses": { - "202": { - "description": "Report accepted", + "201": { + "description": "Participant invited", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/VoipAddParticipantResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -24945,6 +28513,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -24966,10 +28545,21 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { - "description": "Authenticated but not permitted (missing scope or rule violation)", + "description": "`number_restricted` when WhatsApp restricts the number to calling people it has already chatted with and the invitee is not one of them (`Retry-After` gives the seconds until the restriction ends, when known), or the credential is not permitted", "content": { "application/json": { "schema": { @@ -24978,15 +28568,26 @@ "example": { "error": { "type": "permission_error", - "code": "permission_denied", - "message": "The credential does not have access to this resource.", + "code": "number_restricted", + "message": "WhatsApp restricts this number to calling people it has already chatted with. The restriction lifts on its own; follow Retry-After when supplied.", "param": null, "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" }, "data": null, - "docs": "https://docs.polymorfa.com/api/errors#permission-denied" + "docs": "https://docs.polymorfa.com/api/errors#number-restricted" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -25008,10 +28609,21 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { - "description": "`state_conflict` when the call is not live, ended more than 10 minutes ago, or is outside the credential's boundary", + "description": "`unsupported_for_connection` when the session connects through the Cloud API, which has no group calls, or `state_conflict` when the call has no live leg yet", "content": { "application/json": { "schema": { @@ -25020,15 +28632,26 @@ "example": { "error": { "type": "conflict_error", - "code": "state_conflict", - "message": "The request conflicts with the resource state.", + "code": "unsupported_for_connection", + "message": "The session's connection does not support this call operation.", "param": null, "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" }, "data": null, - "docs": "https://docs.polymorfa.com/api/errors#state-conflict" + "docs": "https://docs.polymorfa.com/api/errors#unsupported-for-connection" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "410": { @@ -25050,10 +28673,21 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-gone" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "429": { - "description": "The connection exceeded its report limit: one quality report every 5 seconds or 20 error reports per minute", + "description": "Call setup rate exceeded for this client token, or its concurrency limit is reached", "content": { "application/json": { "schema": { @@ -25071,6 +28705,17 @@ "docs": "https://docs.polymorfa.com/api/errors#rate-limit-exceeded" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -25092,10 +28737,21 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { - "description": "Call authority or report storage is unavailable", + "description": "Call unavailable — the owning pod is at capacity or draining, or call authority cannot be verified", "content": { "application/json": { "schema": { @@ -25113,6 +28769,17 @@ "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -25121,14 +28788,14 @@ } } }, - "/messaging/voip/calls/{id}": { - "delete": { - "operationId": "voipTeardown", + "/messaging/voip/calls/{id}/candidate": { + "post": { + "operationId": "voipCandidate", + "x-internal": true, "tags": [ "VoIP" ], - "summary": "End a call for everyone", - "description": "**Required scope:** `sessions:manage`\n\nEnds the call and closes every connection. Repeating the request succeeds.", + "summary": "Trickle a local ICE candidate to one connection", "security": [ { "BearerAuth": [] @@ -25147,15 +28814,36 @@ "in": "path" } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VoipCandidateRequest" + } + } + } + }, "responses": { - "200": { - "description": "Call ended", + "202": { + "description": "Candidate accepted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -25177,6 +28865,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -25198,6 +28897,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -25219,6 +28929,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -25240,6 +28961,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { @@ -25261,6 +28993,17 @@ "docs": "https://docs.polymorfa.com/api/errors#state-conflict" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "410": { @@ -25282,6 +29025,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-gone" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -25303,6 +29057,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { @@ -25324,46 +29089,72 @@ "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { "content": "sessions:manage" - } + }, + "description": "**Required scope:** `sessions:manage`" } }, - "/messaging/{session}/groups": { + "/messaging/voip/calls/{id}/candidates": { "get": { - "operationId": "listGroups", + "operationId": "voipCandidates", + "x-internal": true, "tags": [ - "Groups" + "VoIP" ], - "summary": "List groups the session is a member of", + "summary": "Poll remote ICE candidates queued for the browser", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "groups:read", + "x-required-scope": "sessions:manage", "parameters": [ { "schema": { - "type": "string" + "type": "string", + "minLength": 1, + "example": "call-123" }, "required": true, - "name": "session", + "name": "id", "in": "path" } ], "responses": { "200": { - "description": "Group list", + "description": "Queued remote candidates (drained on read)", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ListGroupsResponse" + "$ref": "#/components/schemas/VoipCandidatesEnvelope" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -25385,6 +29176,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -25406,6 +29208,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -25427,6 +29240,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -25448,6 +29272,81 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "409": { + "description": "Call not ready — no media session on the owning pod yet", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "conflict_error", + "code": "state_conflict", + "message": "The request conflicts with the resource state.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#state-conflict" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "410": { + "description": "Call ended — the pod that owned its media is gone (pod_lost)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "resource_gone", + "message": "The requested retained resource has expired or was removed.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#resource-gone" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -25469,57 +29368,114 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "503": { + "description": "Call unavailable — the owning pod is at capacity or draining, or call authority cannot be verified", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "service_unavailable", + "message": "A required service is unavailable. Check completion before repeating a write.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API groups:read" + "content": "sessions:manage" }, - "x-protocols": [ - "Linked Device API" - ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:read`" - }, + "description": "**Required scope:** `sessions:manage`" + } + }, + "/messaging/voip/calls/{id}/reports": { "post": { - "operationId": "createGroup", + "operationId": "voipReportCallDiagnostics", "tags": [ - "Groups" + "VoIP" ], - "summary": "Create a new group", + "summary": "Report call diagnostics from your app", + "description": "**Required scope:** `sessions:manage`\n\nRecords quality figures or an error that your app measured for one of its connections. The Console shows the latest quality report and the reported errors of each connection on the call. Reports are optional and are accepted while the call is live and for 10 minutes after it ends. Each connection can send one quality report every 5 seconds and 20 error reports per minute. At most 200 reports are kept per call: at that limit a quality report replaces the same connection's oldest quality report. A quality report from a connection with no stored quality report, and every error report, is accepted but not stored.", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "groups:manage", + "x-required-scope": "sessions:manage", "parameters": [ { "schema": { - "type": "string" + "type": "string", + "minLength": 1, + "example": "call-123" }, "required": true, - "name": "session", + "name": "id", "in": "path" } ], "requestBody": { + "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateGroupRequest" + "$ref": "#/components/schemas/VoipCallReportRequest" } } } }, "responses": { - "200": { - "description": "Group created", + "202": { + "description": "Report accepted", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateGroupResponse" + "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -25541,6 +29497,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -25562,6 +29529,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -25583,6 +29561,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -25604,6 +29593,113 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "409": { + "description": "`state_conflict` when the call is not live, ended more than 10 minutes ago, or is outside the credential's boundary", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "conflict_error", + "code": "state_conflict", + "message": "The request conflicts with the resource state.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#state-conflict" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "410": { + "description": "Call ended — the pod that owned its media is gone (pod_lost)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "resource_gone", + "message": "The requested retained resource has expired or was removed.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#resource-gone" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "429": { + "description": "The connection exceeded its report limit: one quality report every 5 seconds or 20 error reports per minute", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "rate_limit_error", + "code": "rate_limit_exceeded", + "message": "The request limit was reached. Follow Retry-After when supplied.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#rate-limit-exceeded" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -25625,59 +29721,103 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "503": { + "description": "Call authority or report storage is unavailable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "service_unavailable", + "message": "A required service is unavailable. Check completion before repeating a write.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API groups:manage" - }, - "x-protocols": [ - "Linked Device API" - ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:manage`" + "content": "sessions:manage" + } } }, - "/messaging/{session}/groups/join-info": { - "get": { - "operationId": "getGroupJoinInfo", + "/messaging/voip/calls/{id}": { + "delete": { + "operationId": "voipTeardown", "tags": [ - "Groups" + "VoIP" ], - "summary": "Look up group info for an invite code", + "summary": "End a call for everyone", + "description": "**Required scope:** `sessions:manage`\n\nEnds the call and closes every connection. Repeating the request succeeds.", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "groups:read", + "x-required-scope": "sessions:manage", "parameters": [ - { - "schema": { - "type": "string" - }, - "required": true, - "name": "session", - "in": "path" - }, { "schema": { "type": "string", - "minLength": 1 + "minLength": 1, + "example": "call-123" }, "required": true, - "name": "code", - "in": "query" + "name": "id", + "in": "path" } ], "responses": { "200": { - "description": "Join info", + "description": "Call ended", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetGroupJoinInfoResponse" + "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -25699,6 +29839,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -25720,6 +29871,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -25741,6 +29903,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -25762,6 +29935,81 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "409": { + "description": "Call not ready — no media session on the owning pod yet", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "conflict_error", + "code": "state_conflict", + "message": "The request conflicts with the resource state.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#state-conflict" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "410": { + "description": "Call ended — the pod that owned its media is gone (pod_lost)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "resource_gone", + "message": "The requested retained resource has expired or was removed.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#resource-gone" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -25783,31 +30031,70 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "503": { + "description": "Call unavailable — the owning pod is at capacity or draining, or call authority cannot be verified", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "service_unavailable", + "message": "A required service is unavailable. Check completion before repeating a write.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API groups:read" - }, - "x-protocols": [ - "Linked Device API" - ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:read`" + "content": "sessions:manage" + } } }, - "/messaging/{session}/groups/join": { - "post": { - "operationId": "joinGroup", + "/messaging/{session}/groups": { + "get": { + "operationId": "listGroups", "tags": [ "Groups" ], - "summary": "Join a group by invite code", + "summary": "List groups the session is a member of", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "groups:manage", + "x-required-scope": "groups:read", "parameters": [ { "schema": { @@ -25818,24 +30105,26 @@ "in": "path" } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/JoinGroupRequest" - } - } - } - }, "responses": { "200": { - "description": "Joined group", + "description": "Group list", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/ListGroupsResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -25857,6 +30146,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -25878,6 +30178,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -25899,6 +30210,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -25920,6 +30242,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -25941,31 +30274,40 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } - } - } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + } }, "x-mint": { - "content": "Linked Device API groups:manage" + "content": "Linked Device API groups:read" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:manage`" - } - }, - "/messaging/{session}/groups/{id}": { - "get": { - "operationId": "getGroup", + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:read`" + }, + "post": { + "operationId": "createGroup", "tags": [ "Groups" ], - "summary": "Get group details", + "summary": "Create a new group", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "groups:read", + "x-required-scope": "groups:manage", "parameters": [ { "schema": { @@ -25974,26 +30316,37 @@ "required": true, "name": "session", "in": "path" - }, - { - "schema": { - "type": "string", - "pattern": "^[1-9][0-9]{0,18}$" - }, - "required": true, - "name": "id", - "in": "path" } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateGroupRequest" + } + } + } + }, "responses": { "200": { - "description": "Group details", + "description": "Group created", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetGroupResponse" + "$ref": "#/components/schemas/CreateGroupResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -26015,6 +30368,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -26036,6 +30400,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -26057,6 +30432,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -26078,6 +30464,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -26099,29 +30496,42 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API groups:read" + "content": "Linked Device API groups:manage" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:read`" - }, - "delete": { - "operationId": "deleteGroup", + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:manage`" + } + }, + "/messaging/{session}/groups/join-info": { + "get": { + "operationId": "getGroupJoinInfo", "tags": [ "Groups" ], - "summary": "Leave a group (DELETE alias)", + "summary": "Look up group info for an invite code", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "groups:manage", + "x-required-scope": "groups:read", "parameters": [ { "schema": { @@ -26134,22 +30544,33 @@ { "schema": { "type": "string", - "pattern": "^[1-9][0-9]{0,18}$" + "minLength": 1 }, "required": true, - "name": "id", - "in": "path" + "name": "code", + "in": "query" } ], "responses": { "200": { - "description": "Left group", + "description": "Join info", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/GetGroupJoinInfoResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -26171,6 +30592,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -26192,6 +30624,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -26213,6 +30656,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -26234,6 +30688,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -26255,25 +30720,36 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API groups:manage" + "content": "Linked Device API groups:read" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:manage`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:read`" } }, - "/messaging/{session}/groups/{id}/leave": { + "/messaging/{session}/groups/join": { "post": { - "operationId": "leaveGroup", + "operationId": "joinGroup", "tags": [ "Groups" ], - "summary": "Leave a group", + "summary": "Join a group by invite code", "security": [ { "BearerAuth": [] @@ -26288,26 +30764,37 @@ "required": true, "name": "session", "in": "path" - }, - { - "schema": { - "type": "string", - "pattern": "^[1-9][0-9]{0,18}$" - }, - "required": true, - "name": "id", - "in": "path" } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/JoinGroupRequest" + } + } + } + }, "responses": { "200": { - "description": "Left group", + "description": "Joined group", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -26329,6 +30816,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -26350,6 +30848,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -26371,6 +30880,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -26392,6 +30912,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -26413,6 +30944,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -26425,19 +30967,19 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:manage`" } }, - "/messaging/{session}/groups/{id}/subject": { - "put": { - "operationId": "setGroupSubject", + "/messaging/{session}/groups/{id}": { + "get": { + "operationId": "getGroup", "tags": [ "Groups" ], - "summary": "Set group subject (name)", + "summary": "Get group details", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "groups:manage", + "x-required-scope": "groups:read", "parameters": [ { "schema": { @@ -26457,24 +30999,26 @@ "in": "path" } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SetGroupFieldRequest" - } - } - } - }, "responses": { "200": { - "description": "Subject updated", + "description": "Group details", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/GetGroupResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -26496,6 +31040,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -26517,6 +31072,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -26538,6 +31104,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -26559,6 +31136,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -26580,25 +31168,34 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API groups:manage" + "content": "Linked Device API groups:read" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:manage`" - } - }, - "/messaging/{session}/groups/{id}/description": { - "put": { - "operationId": "setGroupDescription", + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:read`" + }, + "delete": { + "operationId": "deleteGroup", "tags": [ "Groups" ], - "summary": "Set group description", + "summary": "Leave a group (DELETE alias)", "security": [ { "BearerAuth": [] @@ -26624,24 +31221,26 @@ "in": "path" } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SetGroupFieldRequest" - } - } - } - }, "responses": { "200": { - "description": "Description updated", + "description": "Left group", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -26663,6 +31262,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -26684,6 +31294,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -26705,6 +31326,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -26726,6 +31358,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -26747,6 +31390,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -26759,19 +31413,19 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:manage`" } }, - "/messaging/{session}/groups/{id}/invite-code": { - "get": { - "operationId": "getGroupInviteCode", + "/messaging/{session}/groups/{id}/leave": { + "post": { + "operationId": "leaveGroup", "tags": [ "Groups" ], - "summary": "Get the current group invite code", + "summary": "Leave a group", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "groups:read", + "x-required-scope": "groups:manage", "parameters": [ { "schema": { @@ -26793,13 +31447,24 @@ ], "responses": { "200": { - "description": "Invite code", + "description": "Left group", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetGroupInviteCodeResponse" + "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -26821,6 +31486,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -26842,6 +31518,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -26863,6 +31550,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -26884,6 +31582,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -26905,25 +31614,36 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API groups:read" + "content": "Linked Device API groups:manage" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:read`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:manage`" } }, - "/messaging/{session}/groups/{id}/invite-code/revoke": { - "post": { - "operationId": "revokeGroupInviteCode", + "/messaging/{session}/groups/{id}/subject": { + "put": { + "operationId": "setGroupSubject", "tags": [ "Groups" ], - "summary": "Revoke the current invite code and generate a new one", + "summary": "Set group subject (name)", "security": [ { "BearerAuth": [] @@ -26949,15 +31669,35 @@ "in": "path" } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SetGroupFieldRequest" + } + } + } + }, "responses": { "200": { - "description": "New invite code", + "description": "Subject updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/RevokeGroupInviteCodeResponse" + "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -26979,6 +31719,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -27000,6 +31751,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -27021,6 +31783,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -27042,6 +31815,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -27063,6 +31847,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -27075,19 +31870,19 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:manage`" } }, - "/messaging/{session}/groups/{id}/participants": { - "get": { - "operationId": "getGroupParticipants", + "/messaging/{session}/groups/{id}/description": { + "put": { + "operationId": "setGroupDescription", "tags": [ "Groups" ], - "summary": "List group participants", + "summary": "Set group description", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "groups:read", + "x-required-scope": "groups:manage", "parameters": [ { "schema": { @@ -27107,15 +31902,35 @@ "in": "path" } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SetGroupFieldRequest" + } + } + } + }, "responses": { "200": { - "description": "Participant list", + "description": "Description updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetGroupParticipantsResponse" + "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -27137,6 +31952,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -27158,6 +31984,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -27179,6 +32016,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -27200,6 +32048,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -27221,31 +32080,42 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API groups:read" + "content": "Linked Device API groups:manage" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:read`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:manage`" } }, - "/messaging/{session}/groups/{id}/participants/add": { - "post": { - "operationId": "addGroupParticipants", + "/messaging/{session}/groups/{id}/invite-code": { + "get": { + "operationId": "getGroupInviteCode", "tags": [ "Groups" ], - "summary": "Add participants to a group", + "summary": "Get the current group invite code", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "groups:manage", + "x-required-scope": "groups:read", "parameters": [ { "schema": { @@ -27265,24 +32135,26 @@ "in": "path" } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/GroupParticipantsRequest" - } - } - } - }, "responses": { "200": { - "description": "Participants added", + "description": "Invite code", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/GetGroupInviteCodeResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -27304,6 +32176,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -27325,6 +32208,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -27346,6 +32240,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -27367,6 +32272,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -27388,25 +32304,36 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API groups:manage" + "content": "Linked Device API groups:read" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:manage`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:read`" } }, - "/messaging/{session}/groups/{id}/participants/remove": { + "/messaging/{session}/groups/{id}/invite-code/revoke": { "post": { - "operationId": "removeGroupParticipants", + "operationId": "revokeGroupInviteCode", "tags": [ "Groups" ], - "summary": "Remove participants from a group", + "summary": "Revoke the current invite code and generate a new one", "security": [ { "BearerAuth": [] @@ -27432,24 +32359,26 @@ "in": "path" } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/GroupParticipantsRequest" - } - } - } - }, "responses": { "200": { - "description": "Participants removed", + "description": "New invite code", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/RevokeGroupInviteCodeResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -27471,6 +32400,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -27492,6 +32432,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -27513,6 +32464,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -27534,6 +32496,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -27555,6 +32528,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -27567,19 +32551,19 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:manage`" } }, - "/messaging/{session}/groups/{id}/admin/promote": { - "post": { - "operationId": "promoteGroupAdmin", + "/messaging/{session}/groups/{id}/participants": { + "get": { + "operationId": "getGroupParticipants", "tags": [ "Groups" ], - "summary": "Promote participants to admin", + "summary": "List group participants", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "groups:manage", + "x-required-scope": "groups:read", "parameters": [ { "schema": { @@ -27599,24 +32583,26 @@ "in": "path" } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/GroupParticipantsRequest" - } - } - } - }, "responses": { "200": { - "description": "Participants promoted", + "description": "Participant list", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/GetGroupParticipantsResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -27638,6 +32624,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -27659,6 +32656,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -27680,6 +32688,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -27701,6 +32720,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -27722,25 +32752,36 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API groups:manage" + "content": "Linked Device API groups:read" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:manage`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:read`" } }, - "/messaging/{session}/groups/{id}/admin/demote": { + "/messaging/{session}/groups/{id}/participants/add": { "post": { - "operationId": "demoteGroupAdmin", + "operationId": "addGroupParticipants", "tags": [ "Groups" ], - "summary": "Demote admin participants", + "summary": "Add participants to a group", "security": [ { "BearerAuth": [] @@ -27777,13 +32818,24 @@ }, "responses": { "200": { - "description": "Participants demoted", + "description": "Participants added", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -27805,6 +32857,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -27826,6 +32889,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -27847,6 +32921,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -27868,6 +32953,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -27889,6 +32985,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -27901,13 +33008,13 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:manage`" } }, - "/messaging/{session}/groups/{id}/picture": { - "put": { - "operationId": "setGroupPicture", + "/messaging/{session}/groups/{id}/participants/remove": { + "post": { + "operationId": "removeGroupParticipants", "tags": [ "Groups" ], - "summary": "Set the group profile picture", + "summary": "Remove participants from a group", "security": [ { "BearerAuth": [] @@ -27937,20 +33044,31 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SetGroupPictureRequest" + "$ref": "#/components/schemas/GroupParticipantsRequest" } } } }, "responses": { "200": { - "description": "Picture updated", + "description": "Participants removed", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -27972,6 +33090,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -27993,6 +33122,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -28014,6 +33154,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -28035,6 +33186,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -28056,6 +33218,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -28068,13 +33241,13 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:manage`" } }, - "/messaging/{session}/groups/{id}/settings/info-edit": { - "put": { - "operationId": "setGroupInfoEditing", + "/messaging/{session}/groups/{id}/admin/promote": { + "post": { + "operationId": "promoteGroupAdmin", "tags": [ "Groups" ], - "summary": "Choose who can edit group information", + "summary": "Promote participants to admin", "security": [ { "BearerAuth": [] @@ -28104,20 +33277,31 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GroupAdminOnlySettingRequest" + "$ref": "#/components/schemas/GroupParticipantsRequest" } } } }, "responses": { "200": { - "description": "Group information permission updated", + "description": "Participants promoted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -28139,6 +33323,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -28160,6 +33355,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -28181,6 +33387,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -28202,6 +33419,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -28223,6 +33451,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -28235,13 +33474,13 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:manage`" } }, - "/messaging/{session}/groups/{id}/settings/messages": { - "put": { - "operationId": "setGroupMessaging", + "/messaging/{session}/groups/{id}/admin/demote": { + "post": { + "operationId": "demoteGroupAdmin", "tags": [ "Groups" ], - "summary": "Choose who can send group messages", + "summary": "Demote admin participants", "security": [ { "BearerAuth": [] @@ -28271,20 +33510,31 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GroupAdminOnlySettingRequest" + "$ref": "#/components/schemas/GroupParticipantsRequest" } } } }, "responses": { "200": { - "description": "Group messaging permission updated", + "description": "Participants demoted", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -28306,6 +33556,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -28327,6 +33588,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -28348,6 +33620,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -28369,6 +33652,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -28390,6 +33684,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -28402,13 +33707,13 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:manage`" } }, - "/messaging/{session}/groups/{id}/settings/member-add": { + "/messaging/{session}/groups/{id}/picture": { "put": { - "operationId": "setGroupMemberAddMode", + "operationId": "setGroupPicture", "tags": [ "Groups" ], - "summary": "Choose who can add group members", + "summary": "Set the group profile picture", "security": [ { "BearerAuth": [] @@ -28438,20 +33743,31 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GroupMemberAddModeRequest" + "$ref": "#/components/schemas/SetGroupPictureRequest" } } } }, "responses": { "200": { - "description": "Group member-add mode updated", + "description": "Picture updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -28473,6 +33789,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -28494,6 +33821,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -28515,6 +33853,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -28536,6 +33885,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -28557,6 +33917,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -28569,13 +33940,13 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:manage`" } }, - "/messaging/{session}/groups/{id}/settings/join-approval": { + "/messaging/{session}/groups/{id}/settings/info-edit": { "put": { - "operationId": "setGroupJoinApproval", + "operationId": "setGroupInfoEditing", "tags": [ "Groups" ], - "summary": "Require admin approval for invite-link joins", + "summary": "Choose who can edit group information", "security": [ { "BearerAuth": [] @@ -28605,20 +33976,31 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GroupJoinApprovalRequest" + "$ref": "#/components/schemas/GroupAdminOnlySettingRequest" } } } }, "responses": { "200": { - "description": "Group join approval updated", + "description": "Group information permission updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -28640,6 +34022,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -28661,6 +34054,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -28682,6 +34086,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -28703,6 +34118,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -28724,6 +34150,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -28736,19 +34173,19 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:manage`" } }, - "/messaging/{session}/contacts": { - "get": { - "operationId": "listContacts", + "/messaging/{session}/groups/{id}/settings/messages": { + "put": { + "operationId": "setGroupMessaging", "tags": [ - "Contacts" + "Groups" ], - "summary": "List all contacts known to the session", + "summary": "Choose who can send group messages", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "contacts:read", + "x-required-scope": "groups:manage", "parameters": [ { "schema": { @@ -28757,17 +34194,46 @@ "required": true, "name": "session", "in": "path" + }, + { + "schema": { + "type": "string", + "pattern": "^[1-9][0-9]{0,18}$" + }, + "required": true, + "name": "id", + "in": "path" } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GroupAdminOnlySettingRequest" + } + } + } + }, "responses": { "200": { - "description": "Contact list", + "description": "Group messaging permission updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ListContactsResponse" + "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -28789,6 +34255,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -28810,6 +34287,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -28831,6 +34319,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -28852,6 +34351,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -28873,31 +34383,42 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API contacts:read" + "content": "Linked Device API groups:manage" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `contacts:read`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:manage`" } }, - "/messaging/{session}/contacts/check": { - "get": { - "operationId": "checkContacts", + "/messaging/{session}/groups/{id}/settings/member-add": { + "put": { + "operationId": "setGroupMemberAddMode", "tags": [ - "Contacts" + "Groups" ], - "summary": "Check if phone numbers are registered on WhatsApp", + "summary": "Choose who can add group members", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "contacts:read", + "x-required-scope": "groups:manage", "parameters": [ { "schema": { @@ -28909,34 +34430,43 @@ }, { "schema": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "array", - "items": { - "type": "string" - } - } - ] + "type": "string", + "pattern": "^[1-9][0-9]{0,18}$" }, "required": true, - "name": "phone", - "in": "query", - "style": "form", - "explode": false + "name": "id", + "in": "path" } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GroupMemberAddModeRequest" + } + } + } + }, "responses": { "200": { - "description": "Contact check results", + "description": "Group member-add mode updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CheckContactsResponse" + "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -28958,6 +34488,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -28979,6 +34520,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -29000,6 +34552,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -29021,6 +34584,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -29042,31 +34616,42 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API contacts:read" + "content": "Linked Device API groups:manage" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `contacts:read`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:manage`" } }, - "/messaging/{session}/contacts/blocked": { - "get": { - "operationId": "getBlocklist", + "/messaging/{session}/groups/{id}/settings/join-approval": { + "put": { + "operationId": "setGroupJoinApproval", "tags": [ - "Contacts" + "Groups" ], - "summary": "Get blocked contacts", + "summary": "Require admin approval for invite-link joins", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "contacts:read", + "x-required-scope": "groups:manage", "parameters": [ { "schema": { @@ -29075,17 +34660,46 @@ "required": true, "name": "session", "in": "path" + }, + { + "schema": { + "type": "string", + "pattern": "^[1-9][0-9]{0,18}$" + }, + "required": true, + "name": "id", + "in": "path" } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GroupJoinApprovalRequest" + } + } + } + }, "responses": { "200": { - "description": "Blocked contacts", + "description": "Group join approval updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetBlocklistResponse" + "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -29107,6 +34721,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -29128,6 +34753,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -29149,6 +34785,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -29170,6 +34817,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -29191,25 +34849,36 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API contacts:read" + "content": "Linked Device API groups:manage" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `contacts:read`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `groups:manage`" } }, - "/messaging/{session}/contacts/{contactId}": { + "/messaging/{session}/contacts": { "get": { - "operationId": "getContact", + "operationId": "listContacts", "tags": [ "Contacts" ], - "summary": "Get a contact by stable user ID", + "summary": "List all contacts known to the session", "security": [ { "BearerAuth": [] @@ -29224,26 +34893,28 @@ "required": true, "name": "session", "in": "path" - }, - { - "schema": { - "type": "string", - "pattern": "^[1-9][0-9]{0,18}$" - }, - "required": true, - "name": "contactId", - "in": "path" } ], "responses": { "200": { - "description": "Contact details", + "description": "Contact list", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetContactResponse" + "$ref": "#/components/schemas/ListContactsResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -29265,6 +34936,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -29286,6 +34968,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -29307,6 +35000,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -29328,6 +35032,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -29349,6 +35064,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -29361,13 +35087,13 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `contacts:read`" } }, - "/messaging/{session}/contacts/{contactId}/picture": { + "/messaging/{session}/contacts/check": { "get": { - "operationId": "getContactPicture", + "operationId": "checkContacts", "tags": [ "Contacts" ], - "summary": "Get a contact's profile picture URL", + "summary": "Check if phone numbers are registered on WhatsApp", "security": [ { "BearerAuth": [] @@ -29385,23 +35111,45 @@ }, { "schema": { - "type": "string", - "pattern": "^[1-9][0-9]{0,18}$" + "anyOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ] }, "required": true, - "name": "contactId", - "in": "path" + "name": "phone", + "in": "query", + "style": "form", + "explode": false } ], "responses": { "200": { - "description": "Profile picture URL", + "description": "Contact check results", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetContactPictureResponse" + "$ref": "#/components/schemas/CheckContactsResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -29423,6 +35171,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -29444,6 +35203,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -29465,6 +35235,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -29486,6 +35267,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -29507,6 +35299,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -29519,13 +35322,13 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `contacts:read`" } }, - "/messaging/{session}/contacts/{contactId}/info": { + "/messaging/{session}/contacts/blocked": { "get": { - "operationId": "getUserInfo", + "operationId": "getBlocklist", "tags": [ "Contacts" ], - "summary": "Get live WhatsApp user metadata", + "summary": "Get blocked contacts", "security": [ { "BearerAuth": [] @@ -29540,26 +35343,28 @@ "required": true, "name": "session", "in": "path" - }, - { - "schema": { - "type": "string", - "pattern": "^[1-9][0-9]{0,18}$" - }, - "required": true, - "name": "contactId", - "in": "path" } ], "responses": { "200": { - "description": "User metadata", + "description": "Blocked contacts", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetUserInfoResponse" + "$ref": "#/components/schemas/GetBlocklistResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -29581,6 +35386,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -29602,6 +35418,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -29623,6 +35450,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -29644,6 +35482,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -29665,6 +35514,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -29677,13 +35537,13 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `contacts:read`" } }, - "/messaging/{session}/contacts/{contactId}/devices": { + "/messaging/{session}/contacts/{contactId}": { "get": { - "operationId": "getUserDevices", + "operationId": "getContact", "tags": [ "Contacts" ], - "summary": "Get a contact's linked devices", + "summary": "Get a contact by stable user ID", "security": [ { "BearerAuth": [] @@ -29711,13 +35571,24 @@ ], "responses": { "200": { - "description": "Linked devices", + "description": "Contact details", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetUserDevicesResponse" + "$ref": "#/components/schemas/GetContactResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -29739,6 +35610,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -29760,6 +35642,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -29781,6 +35674,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -29802,6 +35706,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -29823,6 +35738,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -29835,13 +35761,13 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `contacts:read`" } }, - "/messaging/{session}/contacts/{contactId}/business-profile": { + "/messaging/{session}/contacts/{contactId}/picture": { "get": { - "operationId": "getBusinessProfile", + "operationId": "getContactPicture", "tags": [ "Contacts" ], - "summary": "Get a WhatsApp business profile", + "summary": "Get a contact's profile picture URL", "security": [ { "BearerAuth": [] @@ -29869,13 +35795,24 @@ ], "responses": { "200": { - "description": "Business profile", + "description": "Profile picture URL", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetBusinessProfileResponse" + "$ref": "#/components/schemas/GetContactPictureResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -29897,6 +35834,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -29918,6 +35866,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -29939,6 +35898,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -29960,6 +35930,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -29981,6 +35962,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -29993,19 +35985,19 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `contacts:read`" } }, - "/messaging/{session}/contacts/{contactId}/block": { - "post": { - "operationId": "blockContact", + "/messaging/{session}/contacts/{contactId}/info": { + "get": { + "operationId": "getUserInfo", "tags": [ "Contacts" ], - "summary": "Block a contact", + "summary": "Get live WhatsApp user metadata", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "contacts:manage", + "x-required-scope": "contacts:read", "parameters": [ { "schema": { @@ -30027,13 +36019,24 @@ ], "responses": { "200": { - "description": "Contact blocked", + "description": "User metadata", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/GetUserInfoResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -30055,6 +36058,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -30076,6 +36090,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -30097,6 +36122,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -30118,6 +36154,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -30139,31 +36186,42 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API contacts:manage" + "content": "Linked Device API contacts:read" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `contacts:manage`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `contacts:read`" } }, - "/messaging/{session}/contacts/{contactId}/unblock": { - "post": { - "operationId": "unblockContact", + "/messaging/{session}/contacts/{contactId}/devices": { + "get": { + "operationId": "getUserDevices", "tags": [ "Contacts" ], - "summary": "Unblock a contact", + "summary": "Get a contact's linked devices", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "contacts:manage", + "x-required-scope": "contacts:read", "parameters": [ { "schema": { @@ -30185,13 +36243,24 @@ ], "responses": { "200": { - "description": "Contact unblocked", + "description": "Linked devices", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/GetUserDevicesResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -30213,6 +36282,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -30234,6 +36314,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -30255,6 +36346,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -30276,6 +36378,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -30297,31 +36410,42 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API contacts:manage" + "content": "Linked Device API contacts:read" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `contacts:manage`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `contacts:read`" } }, - "/messaging/{session}/business/linked-accounts": { + "/messaging/{session}/contacts/{contactId}/business-profile": { "get": { - "operationId": "getBusinessLinkedAccounts", + "operationId": "getBusinessProfile", "tags": [ - "Business App" + "Contacts" ], - "summary": "Read live accounts linked to the connected WhatsApp Business App", + "summary": "Get a WhatsApp business profile", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "business:read", + "x-required-scope": "contacts:read", "parameters": [ { "schema": { @@ -30330,17 +36454,37 @@ "required": true, "name": "session", "in": "path" + }, + { + "schema": { + "type": "string", + "pattern": "^[1-9][0-9]{0,18}$" + }, + "required": true, + "name": "contactId", + "in": "path" } ], "responses": { "200": { - "description": "Live linked-account state", + "description": "Business profile", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetBusinessLinkedAccountsResponse" + "$ref": "#/components/schemas/GetBusinessProfileResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -30362,6 +36506,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -30383,6 +36538,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -30404,6 +36570,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -30425,6 +36602,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -30446,31 +36634,42 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API business:read" + "content": "Linked Device API contacts:read" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `business:read`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `contacts:read`" } }, - "/messaging/{session}/business/eligibility": { - "get": { - "operationId": "getBusinessEligibility", + "/messaging/{session}/contacts/{contactId}/block": { + "post": { + "operationId": "blockContact", "tags": [ - "Business App" + "Contacts" ], - "summary": "Read live feature eligibility for the connected WhatsApp Business App", + "summary": "Block a contact", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "business:read", + "x-required-scope": "contacts:manage", "parameters": [ { "schema": { @@ -30479,17 +36678,37 @@ "required": true, "name": "session", "in": "path" + }, + { + "schema": { + "type": "string", + "pattern": "^[1-9][0-9]{0,18}$" + }, + "required": true, + "name": "contactId", + "in": "path" } ], "responses": { "200": { - "description": "Live business feature eligibility", + "description": "Contact blocked", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetBusinessEligibilityResponse" + "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -30511,6 +36730,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -30532,6 +36762,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -30553,6 +36794,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -30574,6 +36826,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -30595,31 +36858,42 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API business:read" + "content": "Linked Device API contacts:manage" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `business:read`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `contacts:manage`" } }, - "/messaging/{session}/business/profile": { - "get": { - "operationId": "getOwnBusinessProfile", + "/messaging/{session}/contacts/{contactId}/unblock": { + "post": { + "operationId": "unblockContact", "tags": [ - "Business App" + "Contacts" ], - "summary": "Read the connected WhatsApp Business App profile", + "summary": "Unblock a contact", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "profile:read", + "x-required-scope": "contacts:manage", "parameters": [ { "schema": { @@ -30628,17 +36902,37 @@ "required": true, "name": "session", "in": "path" + }, + { + "schema": { + "type": "string", + "pattern": "^[1-9][0-9]{0,18}$" + }, + "required": true, + "name": "contactId", + "in": "path" } ], "responses": { "200": { - "description": "Live business profile", + "description": "Contact unblocked", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetOwnBusinessProfileResponse" + "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -30660,6 +36954,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -30681,6 +36986,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -30702,6 +37018,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -30723,6 +37050,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -30744,29 +37082,42 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API profile:read" + "content": "Linked Device API contacts:manage" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:read`" - }, - "patch": { - "operationId": "updateBusinessProfile", + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `contacts:manage`" + } + }, + "/messaging/{session}/business/linked-accounts": { + "get": { + "operationId": "getBusinessLinkedAccounts", "tags": [ "Business App" ], - "summary": "Update the connected WhatsApp Business App profile", + "summary": "Read live accounts linked to the connected WhatsApp Business App", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "profile:write", + "x-required-scope": "business:read", "parameters": [ { "schema": { @@ -30777,25 +37128,26 @@ "in": "path" } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BusinessProfileUpdateRequest" - } - } - } - }, "responses": { "200": { - "description": "Profile updated", + "description": "Live linked-account state", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateBusinessProfileResponse" + "$ref": "#/components/schemas/GetBusinessLinkedAccountsResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -30817,6 +37169,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -30838,6 +37201,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -30859,6 +37233,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -30880,6 +37265,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -30901,31 +37297,42 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API profile:write" + "content": "Linked Device API business:read" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `business:read`" } }, - "/messaging/{session}/business/profile/cover-photo": { - "put": { - "operationId": "setBusinessCoverPhoto", + "/messaging/{session}/business/eligibility": { + "get": { + "operationId": "getBusinessEligibility", "tags": [ "Business App" ], - "summary": "Set the connected WhatsApp Business App cover photo", + "summary": "Read live feature eligibility for the connected WhatsApp Business App", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "profile:write", + "x-required-scope": "business:read", "parameters": [ { "schema": { @@ -30936,25 +37343,26 @@ "in": "path" } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BusinessCoverPhotoRequest" - } - } - } - }, "responses": { "200": { - "description": "Cover photo set", + "description": "Live business feature eligibility", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SetBusinessCoverPhotoResponse" + "$ref": "#/components/schemas/GetBusinessEligibilityResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -30976,6 +37384,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -30997,6 +37416,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -31018,6 +37448,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -31039,6 +37480,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -31060,31 +37512,42 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API profile:write" + "content": "Linked Device API business:read" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `business:read`" } }, - "/messaging/{session}/business/profile/cover-photo/{coverPhotoId}": { - "delete": { - "operationId": "deleteBusinessCoverPhoto", + "/messaging/{session}/business/profile": { + "get": { + "operationId": "getOwnBusinessProfile", "tags": [ "Business App" ], - "summary": "Delete the connected WhatsApp Business App cover photo", + "summary": "Read the connected WhatsApp Business App profile", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "profile:write", + "x-required-scope": "profile:read", "parameters": [ { "schema": { @@ -31093,27 +37556,28 @@ "required": true, "name": "session", "in": "path" - }, - { - "schema": { - "type": "string", - "minLength": 1, - "maxLength": 256 - }, - "required": true, - "name": "coverPhotoId", - "in": "path" } ], "responses": { "200": { - "description": "Cover photo removed", + "description": "Live business profile", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeleteBusinessCoverPhotoResponse" + "$ref": "#/components/schemas/GetOwnBusinessProfileResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -31135,6 +37599,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -31156,6 +37631,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -31177,6 +37663,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -31198,6 +37695,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -31219,31 +37727,40 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API profile:write" + "content": "Linked Device API profile:read" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" - } - }, - "/messaging/{session}/business/compliance": { - "get": { - "operationId": "getBusinessMerchantCompliance", + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:read`" + }, + "patch": { + "operationId": "updateBusinessProfile", "tags": [ "Business App" ], - "summary": "Read merchant compliance details for the connected WhatsApp Business App account", + "summary": "Update the connected WhatsApp Business App profile", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "profile:read", + "x-required-scope": "profile:write", "parameters": [ { "schema": { @@ -31254,15 +37771,36 @@ "in": "path" } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BusinessProfileUpdateRequest" + } + } + } + }, "responses": { "200": { - "description": "Live merchant compliance details", + "description": "Profile updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetBusinessMerchantComplianceResponse" + "$ref": "#/components/schemas/UpdateBusinessProfileResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -31284,6 +37822,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -31305,6 +37854,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -31326,6 +37886,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -31347,6 +37918,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -31368,23 +37950,36 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API profile:read" + "content": "Linked Device API profile:write" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:read`" - }, + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" + } + }, + "/messaging/{session}/business/profile/cover-photo": { "put": { - "operationId": "setBusinessMerchantCompliance", + "operationId": "setBusinessCoverPhoto", "tags": [ "Business App" ], - "summary": "Replace merchant compliance details for the connected WhatsApp Business App account", + "summary": "Set the connected WhatsApp Business App cover photo", "security": [ { "BearerAuth": [] @@ -31406,20 +38001,31 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BusinessMerchantCompliance" + "$ref": "#/components/schemas/BusinessCoverPhotoRequest" } } } }, "responses": { "200": { - "description": "Merchant compliance details updated", + "description": "Cover photo set", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SetBusinessMerchantComplianceResponse" + "$ref": "#/components/schemas/SetBusinessCoverPhotoResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -31441,6 +38047,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -31462,6 +38079,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -31483,6 +38111,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -31504,6 +38143,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -31525,6 +38175,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -31537,13 +38198,13 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" } }, - "/messaging/{session}/business/quick-replies": { - "post": { - "operationId": "createBusinessQuickReply", + "/messaging/{session}/business/profile/cover-photo/{coverPhotoId}": { + "delete": { + "operationId": "deleteBusinessCoverPhoto", "tags": [ "Business App" ], - "summary": "Create a WhatsApp Business App quick reply", + "summary": "Delete the connected WhatsApp Business App cover photo", "security": [ { "BearerAuth": [] @@ -31558,27 +38219,38 @@ "required": true, "name": "session", "in": "path" + }, + { + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 256 + }, + "required": true, + "name": "coverPhotoId", + "in": "path" } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BusinessQuickReplyMutation" - } - } - } - }, "responses": { "200": { - "description": "Quick reply created", + "description": "Cover photo removed", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateBusinessQuickReplyResponse" + "$ref": "#/components/schemas/DeleteBusinessCoverPhotoResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -31600,6 +38272,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -31621,6 +38304,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -31642,6 +38336,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -31663,6 +38368,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -31684,6 +38400,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -31694,13 +38421,15 @@ "Linked Device API" ], "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" - }, + } + }, + "/messaging/{session}/business/compliance": { "get": { - "operationId": "listBusinessQuickReplies", + "operationId": "getBusinessMerchantCompliance", "tags": [ "Business App" ], - "summary": "List remembered WhatsApp Business App quick replies", + "summary": "Read merchant compliance details for the connected WhatsApp Business App account", "security": [ { "BearerAuth": [] @@ -31719,13 +38448,24 @@ ], "responses": { "200": { - "description": "Best-effort quick reply list", + "description": "Live merchant compliance details", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ListBusinessQuickRepliesResponse" + "$ref": "#/components/schemas/GetBusinessMerchantComplianceResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -31747,6 +38487,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -31768,6 +38519,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -31789,6 +38551,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -31810,6 +38583,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -31831,6 +38615,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -31841,15 +38636,13 @@ "Linked Device API" ], "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:read`" - } - }, - "/messaging/{session}/business/quick-replies/{quickReplyId}": { + }, "put": { - "operationId": "setBusinessQuickReply", + "operationId": "setBusinessMerchantCompliance", "tags": [ "Business App" ], - "summary": "Replace a WhatsApp Business App quick reply", + "summary": "Replace merchant compliance details for the connected WhatsApp Business App account", "security": [ { "BearerAuth": [] @@ -31864,17 +38657,6 @@ "required": true, "name": "session", "in": "path" - }, - { - "schema": { - "type": "string", - "minLength": 1, - "maxLength": 20, - "pattern": "^[0-9]+$" - }, - "required": true, - "name": "quickReplyId", - "in": "path" } ], "requestBody": { @@ -31882,20 +38664,31 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BusinessQuickReplyMutation" + "$ref": "#/components/schemas/BusinessMerchantCompliance" } } } }, "responses": { "200": { - "description": "Quick reply replaced", + "description": "Merchant compliance details updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SetBusinessQuickReplyResponse" + "$ref": "#/components/schemas/SetBusinessMerchantComplianceResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -31917,6 +38710,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -31938,6 +38742,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -31959,6 +38774,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -31980,6 +38806,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -32001,6 +38838,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -32011,13 +38859,15 @@ "Linked Device API" ], "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" - }, - "delete": { - "operationId": "deleteBusinessQuickReply", + } + }, + "/messaging/{session}/business/quick-replies": { + "post": { + "operationId": "createBusinessQuickReply", "tags": [ "Business App" ], - "summary": "Delete a WhatsApp Business App quick reply", + "summary": "Create a WhatsApp Business App quick reply", "security": [ { "BearerAuth": [] @@ -32032,28 +38882,38 @@ "required": true, "name": "session", "in": "path" - }, - { - "schema": { - "type": "string", - "minLength": 1, - "maxLength": 20, - "pattern": "^[0-9]+$" - }, - "required": true, - "name": "quickReplyId", - "in": "path" } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BusinessQuickReplyMutation" + } + } + } + }, "responses": { "200": { - "description": "Quick reply deleted", + "description": "Quick reply created", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeleteBusinessQuickReplyResponse" + "$ref": "#/components/schemas/CreateBusinessQuickReplyResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -32075,6 +38935,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -32096,6 +38967,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -32117,6 +38999,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -32138,6 +39031,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -32159,6 +39063,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -32169,21 +39084,19 @@ "Linked Device API" ], "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" - } - }, - "/messaging/{session}/business/catalog": { + }, "get": { - "operationId": "getBusinessCatalog", + "operationId": "listBusinessQuickReplies", "tags": [ "Business App" ], - "summary": "Read a WhatsApp Business App catalog", + "summary": "List remembered WhatsApp Business App quick replies", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "business:read", + "x-required-scope": "profile:read", "parameters": [ { "schema": { @@ -32192,66 +39105,28 @@ "required": true, "name": "session", "in": "path" - }, - { - "schema": { - "type": "string", - "maxLength": 256, - "pattern": "^[1-9][0-9]{0,18}$" - }, - "required": true, - "name": "id", - "in": "query" - }, - { - "schema": { - "type": "string", - "maxLength": 2048 - }, - "required": false, - "name": "after", - "in": "query" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 1024 - }, - "required": false, - "name": "width", - "in": "query" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 1024 - }, - "required": false, - "name": "height", - "in": "query" } ], "responses": { "200": { - "description": "Live catalog page", + "description": "Best-effort quick reply list", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetBusinessCatalogResponse" + "$ref": "#/components/schemas/ListBusinessQuickRepliesResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -32273,6 +39148,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -32294,6 +39180,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -32315,6 +39212,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -32336,6 +39244,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -32357,44 +39276,36 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } - } - }, - "502": { - "description": "Upstream WhatsApp request failed or its response could not be mapped", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "upstream_error", - "code": "upstream_failure", - "message": "The messaging provider could not complete the request.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#upstream-failure" - } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } } }, "x-mint": { - "content": "Linked Device API business:read" + "content": "Linked Device API profile:read" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `business:read`" - }, - "post": { - "operationId": "createBusinessCatalog", + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:read`" + } + }, + "/messaging/{session}/business/quick-replies/{quickReplyId}": { + "put": { + "operationId": "setBusinessQuickReply", "tags": [ "Business App" ], - "summary": "Create a catalog for the connected WhatsApp Business App account", + "summary": "Replace a WhatsApp Business App quick reply", "security": [ { "BearerAuth": [] @@ -32409,17 +39320,49 @@ "required": true, "name": "session", "in": "path" + }, + { + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 20, + "pattern": "^[0-9]+$" + }, + "required": true, + "name": "quickReplyId", + "in": "path" } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BusinessQuickReplyMutation" + } + } + } + }, "responses": { "200": { - "description": "Catalog created", + "description": "Quick reply replaced", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateBusinessCatalogResponse" + "$ref": "#/components/schemas/SetBusinessQuickReplyResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -32441,6 +39384,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -32462,6 +39416,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -32483,6 +39448,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -32504,6 +39480,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -32525,6 +39512,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -32535,15 +39533,13 @@ "Linked Device API" ], "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" - } - }, - "/messaging/{session}/business/catalog/cart": { - "patch": { - "operationId": "setBusinessCartEnabled", + }, + "delete": { + "operationId": "deleteBusinessQuickReply", "tags": [ "Business App" ], - "summary": "Enable or disable the connected WhatsApp Business App catalog cart", + "summary": "Delete a WhatsApp Business App quick reply", "security": [ { "BearerAuth": [] @@ -32558,27 +39554,39 @@ "required": true, "name": "session", "in": "path" + }, + { + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 20, + "pattern": "^[0-9]+$" + }, + "required": true, + "name": "quickReplyId", + "in": "path" } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BusinessCartSettingRequest" - } - } - } - }, "responses": { "200": { - "description": "Cart setting updated", + "description": "Quick reply deleted", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SetBusinessCartEnabledResponse" + "$ref": "#/components/schemas/DeleteBusinessQuickReplyResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -32600,6 +39608,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -32621,6 +39640,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -32642,6 +39672,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -32663,6 +39704,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -32684,6 +39736,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -32696,13 +39759,13 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" } }, - "/messaging/{session}/business/products/{productId}": { + "/messaging/{session}/business/catalog": { "get": { - "operationId": "getBusinessProduct", + "operationId": "getBusinessCatalog", "tags": [ "Business App" ], - "summary": "Read a WhatsApp Business App product", + "summary": "Read a WhatsApp Business App catalog", "security": [ { "BearerAuth": [] @@ -32721,32 +39784,73 @@ { "schema": { "type": "string", - "maxLength": 256 + "maxLength": 256, + "pattern": "^[1-9][0-9]{0,18}$" }, "required": true, - "name": "productId", - "in": "path" + "name": "id", + "in": "query" }, { "schema": { "type": "string", - "maxLength": 256, - "pattern": "^[1-9][0-9]{0,18}$" + "maxLength": 2048 }, - "required": true, - "name": "id", + "required": false, + "name": "after", + "in": "query" + }, + { + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100 + }, + "required": false, + "name": "limit", + "in": "query" + }, + { + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 1024 + }, + "required": false, + "name": "width", + "in": "query" + }, + { + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 1024 + }, + "required": false, + "name": "height", "in": "query" } ], "responses": { "200": { - "description": "Live product", + "description": "Live catalog page", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetBusinessProductResponse" + "$ref": "#/components/schemas/GetBusinessCatalogResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -32768,6 +39872,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -32789,6 +39904,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -32810,6 +39936,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -32831,6 +39968,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -32852,6 +40000,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "502": { @@ -32873,6 +40032,17 @@ "docs": "https://docs.polymorfa.com/api/errors#upstream-failure" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -32884,12 +40054,12 @@ ], "description": "**Protocols:** Linked Device API\n\n**Required scope:** `business:read`" }, - "put": { - "operationId": "updateBusinessProduct", + "post": { + "operationId": "createBusinessCatalog", "tags": [ "Business App" ], - "summary": "Replace a product in the connected WhatsApp Business App catalog", + "summary": "Create a catalog for the connected WhatsApp Business App account", "security": [ { "BearerAuth": [] @@ -32904,36 +40074,28 @@ "required": true, "name": "session", "in": "path" - }, - { - "schema": { - "type": "string", - "maxLength": 256 - }, - "required": true, - "name": "productId", - "in": "path" } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BusinessProductMutationRequest" - } - } - } - }, "responses": { "200": { - "description": "Product updated", + "description": "Catalog created", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateBusinessProductResponse" + "$ref": "#/components/schemas/CreateBusinessCatalogResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -32955,6 +40117,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -32976,6 +40149,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -32997,6 +40181,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -33018,6 +40213,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -33039,6 +40245,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -33049,13 +40266,15 @@ "Linked Device API" ], "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" - }, - "delete": { - "operationId": "deleteBusinessProduct", + } + }, + "/messaging/{session}/business/catalog/cart": { + "patch": { + "operationId": "setBusinessCartEnabled", "tags": [ "Business App" ], - "summary": "Delete a product from the connected WhatsApp Business App catalog", + "summary": "Enable or disable the connected WhatsApp Business App catalog cart", "security": [ { "BearerAuth": [] @@ -33070,26 +40289,38 @@ "required": true, "name": "session", "in": "path" - }, - { - "schema": { - "type": "string", - "maxLength": 256 - }, - "required": true, - "name": "productId", - "in": "path" } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BusinessCartSettingRequest" + } + } + } + }, "responses": { "200": { - "description": "Product deleted", + "description": "Cart setting updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeleteBusinessProductResponse" + "$ref": "#/components/schemas/SetBusinessCartEnabledResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -33111,6 +40342,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -33132,6 +40374,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -33153,6 +40406,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -33174,6 +40438,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -33195,6 +40470,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -33207,19 +40493,19 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" } }, - "/messaging/{session}/business/products": { - "post": { - "operationId": "createBusinessProduct", + "/messaging/{session}/business/products/{productId}": { + "get": { + "operationId": "getBusinessProduct", "tags": [ "Business App" ], - "summary": "Create a product in the connected WhatsApp Business App catalog", + "summary": "Read a WhatsApp Business App product", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "profile:write", + "x-required-scope": "business:read", "parameters": [ { "schema": { @@ -33228,27 +40514,47 @@ "required": true, "name": "session", "in": "path" + }, + { + "schema": { + "type": "string", + "maxLength": 256 + }, + "required": true, + "name": "productId", + "in": "path" + }, + { + "schema": { + "type": "string", + "maxLength": 256, + "pattern": "^[1-9][0-9]{0,18}$" + }, + "required": true, + "name": "id", + "in": "query" } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BusinessProductMutationRequest" - } - } - } - }, "responses": { "200": { - "description": "Product created", + "description": "Live product", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateBusinessProductResponse" + "$ref": "#/components/schemas/GetBusinessProductResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -33270,6 +40576,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -33291,6 +40608,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -33312,6 +40640,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -33333,6 +40672,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -33354,25 +40704,66 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "502": { + "description": "Upstream WhatsApp request failed or its response could not be mapped", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "upstream_error", + "code": "upstream_failure", + "message": "The messaging provider could not complete the request.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#upstream-failure" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API profile:write" + "content": "Linked Device API business:read" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" - } - }, - "/messaging/{session}/business/products/{productId}/visibility": { - "patch": { - "operationId": "setBusinessProductVisibility", + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `business:read`" + }, + "put": { + "operationId": "updateBusinessProduct", "tags": [ "Business App" ], - "summary": "Hide or show a product in the connected WhatsApp Business App catalog", + "summary": "Replace a product in the connected WhatsApp Business App catalog", "security": [ { "BearerAuth": [] @@ -33403,20 +40794,31 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BusinessProductVisibilityRequest" + "$ref": "#/components/schemas/BusinessProductMutationRequest" } } } }, "responses": { "200": { - "description": "Product visibility updated", + "description": "Product updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SetBusinessProductVisibilityResponse" + "$ref": "#/components/schemas/UpdateBusinessProductResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -33438,6 +40840,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -33459,6 +40872,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -33480,6 +40904,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -33501,6 +40936,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -33522,6 +40968,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -33532,15 +40989,13 @@ "Linked Device API" ], "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" - } - }, - "/messaging/{session}/business/products/{productId}/appeal": { - "post": { - "operationId": "appealBusinessProduct", + }, + "delete": { + "operationId": "deleteBusinessProduct", "tags": [ "Business App" ], - "summary": "Request a review of a rejected WhatsApp Business App catalog product", + "summary": "Delete a product from the connected WhatsApp Business App catalog", "security": [ { "BearerAuth": [] @@ -33566,25 +41021,26 @@ "in": "path" } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BusinessCatalogAppealRequest" - } - } - } - }, "responses": { "200": { - "description": "Product appeal submitted", + "description": "Product deleted", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AppealBusinessProductResponse" + "$ref": "#/components/schemas/DeleteBusinessProductResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -33606,6 +41062,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -33627,6 +41094,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -33648,6 +41126,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -33669,6 +41158,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -33690,6 +41190,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -33702,19 +41213,19 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" } }, - "/messaging/{session}/business/collections": { - "get": { - "operationId": "getBusinessCollections", + "/messaging/{session}/business/products": { + "post": { + "operationId": "createBusinessProduct", "tags": [ "Business App" ], - "summary": "Read WhatsApp Business App collections", + "summary": "Create a product in the connected WhatsApp Business App catalog", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "business:read", + "x-required-scope": "profile:write", "parameters": [ { "schema": { @@ -33723,76 +41234,38 @@ "required": true, "name": "session", "in": "path" - }, - { - "schema": { - "type": "string", - "maxLength": 256, - "pattern": "^[1-9][0-9]{0,18}$" - }, - "required": true, - "name": "id", - "in": "query" - }, - { - "schema": { - "type": "string", - "maxLength": 2048 - }, - "required": false, - "name": "after", - "in": "query" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 20 - }, - "required": false, - "name": "collectionLimit", - "in": "query" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100 - }, - "required": false, - "name": "itemLimit", - "in": "query" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 1024 - }, - "required": false, - "name": "width", - "in": "query" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 1024 - }, - "required": false, - "name": "height", - "in": "query" } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BusinessProductMutationRequest" + } + } + } + }, "responses": { "200": { - "description": "Live collection page", + "description": "Product created", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetBusinessCollectionsResponse" + "$ref": "#/components/schemas/CreateBusinessProductResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -33814,6 +41287,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -33835,6 +41319,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -33856,6 +41351,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -33877,6 +41383,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -33898,44 +41415,36 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } - } - }, - "502": { - "description": "Upstream WhatsApp request failed or its response could not be mapped", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "upstream_error", - "code": "upstream_failure", - "message": "The messaging provider could not complete the request.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#upstream-failure" - } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } } }, "x-mint": { - "content": "Linked Device API business:read" + "content": "Linked Device API profile:write" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `business:read`" - }, - "post": { - "operationId": "createBusinessCollection", + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" + } + }, + "/messaging/{session}/business/products/{productId}/visibility": { + "patch": { + "operationId": "setBusinessProductVisibility", "tags": [ "Business App" ], - "summary": "Create a collection in the connected WhatsApp Business App catalog", + "summary": "Hide or show a product in the connected WhatsApp Business App catalog", "security": [ { "BearerAuth": [] @@ -33950,6 +41459,15 @@ "required": true, "name": "session", "in": "path" + }, + { + "schema": { + "type": "string", + "maxLength": 256 + }, + "required": true, + "name": "productId", + "in": "path" } ], "requestBody": { @@ -33957,20 +41475,31 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BusinessCollectionCreateRequest" + "$ref": "#/components/schemas/BusinessProductVisibilityRequest" } } } }, "responses": { "200": { - "description": "Collection created", + "description": "Product visibility updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateBusinessCollectionResponse" + "$ref": "#/components/schemas/SetBusinessProductVisibilityResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -33992,6 +41521,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -34013,6 +41553,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -34034,6 +41585,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -34055,6 +41617,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -34076,6 +41649,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -34088,19 +41672,19 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" } }, - "/messaging/{session}/business/collections/{collectionId}": { - "get": { - "operationId": "getBusinessCollection", + "/messaging/{session}/business/products/{productId}/appeal": { + "post": { + "operationId": "appealBusinessProduct", "tags": [ "Business App" ], - "summary": "Read a WhatsApp Business App collection", + "summary": "Request a review of a rejected WhatsApp Business App catalog product", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "business:read", + "x-required-scope": "profile:write", "parameters": [ { "schema": { @@ -34116,68 +41700,40 @@ "maxLength": 256 }, "required": true, - "name": "collectionId", + "name": "productId", "in": "path" - }, - { - "schema": { - "type": "string", - "maxLength": 256, - "pattern": "^[1-9][0-9]{0,18}$" - }, - "required": true, - "name": "id", - "in": "query" - }, - { - "schema": { - "type": "string", - "maxLength": 2048 - }, - "required": false, - "name": "after", - "in": "query" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100 - }, - "required": false, - "name": "limit", - "in": "query" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 1024 - }, - "required": false, - "name": "width", - "in": "query" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 1024 - }, - "required": false, - "name": "height", - "in": "query" } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BusinessCatalogAppealRequest" + } + } + } + }, "responses": { "200": { - "description": "Live collection", + "description": "Product appeal submitted", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetBusinessCollectionResponse" + "$ref": "#/components/schemas/AppealBusinessProductResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -34199,6 +41755,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -34220,6 +41787,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -34241,6 +41819,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -34262,6 +41851,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -34283,50 +41883,42 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } - } - }, - "502": { - "description": "Upstream WhatsApp request failed or its response could not be mapped", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "upstream_error", - "code": "upstream_failure", - "message": "The messaging provider could not complete the request.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#upstream-failure" - } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } } }, "x-mint": { - "content": "Linked Device API business:read" + "content": "Linked Device API profile:write" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `business:read`" - }, - "patch": { - "operationId": "updateBusinessCollection", + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" + } + }, + "/messaging/{session}/business/collections": { + "get": { + "operationId": "getBusinessCollections", "tags": [ "Business App" ], - "summary": "Update a collection and its product membership", + "summary": "Read WhatsApp Business App collections", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "profile:write", + "x-required-scope": "business:read", "parameters": [ { "schema": { @@ -34339,32 +41931,83 @@ { "schema": { "type": "string", - "maxLength": 256 + "maxLength": 256, + "pattern": "^[1-9][0-9]{0,18}$" }, "required": true, - "name": "collectionId", - "in": "path" + "name": "id", + "in": "query" + }, + { + "schema": { + "type": "string", + "maxLength": 2048 + }, + "required": false, + "name": "after", + "in": "query" + }, + { + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 20 + }, + "required": false, + "name": "collectionLimit", + "in": "query" + }, + { + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100 + }, + "required": false, + "name": "itemLimit", + "in": "query" + }, + { + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 1024 + }, + "required": false, + "name": "width", + "in": "query" + }, + { + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 1024 + }, + "required": false, + "name": "height", + "in": "query" } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BusinessCollectionUpdateRequest" - } - } - } - }, "responses": { "200": { - "description": "Collection updated", + "description": "Live collection page", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateBusinessCollectionResponse" + "$ref": "#/components/schemas/GetBusinessCollectionsResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -34386,6 +42029,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -34407,6 +42061,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -34428,6 +42093,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -34449,6 +42125,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -34470,23 +42157,66 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "502": { + "description": "Upstream WhatsApp request failed or its response could not be mapped", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "upstream_error", + "code": "upstream_failure", + "message": "The messaging provider could not complete the request.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#upstream-failure" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API profile:write" + "content": "Linked Device API business:read" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `business:read`" }, - "delete": { - "operationId": "deleteBusinessCollection", + "post": { + "operationId": "createBusinessCollection", "tags": [ "Business App" ], - "summary": "Delete a collection from the connected WhatsApp Business App catalog", + "summary": "Create a collection in the connected WhatsApp Business App catalog", "security": [ { "BearerAuth": [] @@ -34501,26 +42231,38 @@ "required": true, "name": "session", "in": "path" - }, - { - "schema": { - "type": "string", - "maxLength": 256 - }, - "required": true, - "name": "collectionId", - "in": "path" } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BusinessCollectionCreateRequest" + } + } + } + }, "responses": { "200": { - "description": "Collection deleted", + "description": "Collection created", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeleteBusinessCollectionResponse" + "$ref": "#/components/schemas/CreateBusinessCollectionResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -34542,6 +42284,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -34563,6 +42316,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -34584,6 +42348,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -34605,6 +42380,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -34626,6 +42412,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -34638,19 +42435,19 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" } }, - "/messaging/{session}/business/collections/reorder": { - "post": { - "operationId": "reorderBusinessCollections", + "/messaging/{session}/business/collections/{collectionId}": { + "get": { + "operationId": "getBusinessCollection", "tags": [ "Business App" ], - "summary": "Reorder connected WhatsApp Business App catalog collections", + "summary": "Read a WhatsApp Business App collection", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "profile:write", + "x-required-scope": "business:read", "parameters": [ { "schema": { @@ -34659,27 +42456,86 @@ "required": true, "name": "session", "in": "path" + }, + { + "schema": { + "type": "string", + "maxLength": 256 + }, + "required": true, + "name": "collectionId", + "in": "path" + }, + { + "schema": { + "type": "string", + "maxLength": 256, + "pattern": "^[1-9][0-9]{0,18}$" + }, + "required": true, + "name": "id", + "in": "query" + }, + { + "schema": { + "type": "string", + "maxLength": 2048 + }, + "required": false, + "name": "after", + "in": "query" + }, + { + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100 + }, + "required": false, + "name": "limit", + "in": "query" + }, + { + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 1024 + }, + "required": false, + "name": "width", + "in": "query" + }, + { + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 1024 + }, + "required": false, + "name": "height", + "in": "query" } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BusinessCollectionReorderRequest" - } - } - } - }, "responses": { "200": { - "description": "Collections reordered", + "description": "Live collection", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReorderBusinessCollectionsResponse" + "$ref": "#/components/schemas/GetBusinessCollectionResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -34701,6 +42557,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -34722,6 +42589,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -34743,6 +42621,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -34764,6 +42653,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -34785,25 +42685,66 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "502": { + "description": "Upstream WhatsApp request failed or its response could not be mapped", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "upstream_error", + "code": "upstream_failure", + "message": "The messaging provider could not complete the request.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#upstream-failure" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API profile:write" + "content": "Linked Device API business:read" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" - } - }, - "/messaging/{session}/business/collections/{collectionId}/appeal": { - "post": { - "operationId": "appealBusinessCollection", + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `business:read`" + }, + "patch": { + "operationId": "updateBusinessCollection", "tags": [ "Business App" ], - "summary": "Request a review of a rejected WhatsApp Business App catalog collection", + "summary": "Update a collection and its product membership", "security": [ { "BearerAuth": [] @@ -34834,20 +42775,31 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/BusinessCatalogAppealRequest" + "$ref": "#/components/schemas/BusinessCollectionUpdateRequest" } } } }, "responses": { "200": { - "description": "Collection appeal submitted", + "description": "Collection updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AppealBusinessCollectionResponse" + "$ref": "#/components/schemas/UpdateBusinessCollectionResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -34869,6 +42821,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -34890,6 +42853,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -34911,6 +42885,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -34932,6 +42917,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -34953,6 +42949,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -34963,21 +42970,19 @@ "Linked Device API" ], "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" - } - }, - "/messaging/{session}/business/orders/{orderId}/lookup": { - "post": { - "operationId": "getBusinessOrder", + }, + "delete": { + "operationId": "deleteBusinessCollection", "tags": [ "Business App" ], - "summary": "Read WhatsApp Business App order details", + "summary": "Delete a collection from the connected WhatsApp Business App catalog", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "business:read", + "x-required-scope": "profile:write", "parameters": [ { "schema": { @@ -34993,29 +42998,30 @@ "maxLength": 256 }, "required": true, - "name": "orderId", + "name": "collectionId", "in": "path" } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/BusinessOrderLookupRequest" - } - } - } - }, "responses": { "200": { - "description": "Order details", + "description": "Collection deleted", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetBusinessOrderResponse" + "$ref": "#/components/schemas/DeleteBusinessCollectionResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -35037,6 +43043,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -35058,6 +43075,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -35079,6 +43107,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -35100,6 +43139,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -35121,52 +43171,42 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } - } - }, - "502": { - "description": "Upstream WhatsApp request failed or its response could not be mapped", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "upstream_error", - "code": "upstream_failure", - "message": "The messaging provider could not complete the request.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#upstream-failure" - } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } } }, "x-mint": { - "content": "Linked Device API business:read" + "content": "Linked Device API profile:write" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `business:read`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" } }, - "/messaging/{session}/identities/resolve": { - "get": { - "operationId": "resolveIdentity", + "/messaging/{session}/business/collections/reorder": { + "post": { + "operationId": "reorderBusinessCollections", "tags": [ - "Identities" + "Business App" ], - "summary": "Resolve a user alias to a stable user ID", + "summary": "Reorder connected WhatsApp Business App catalog collections", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "contacts:read", + "x-required-scope": "profile:write", "parameters": [ { "schema": { @@ -35175,53 +43215,38 @@ "required": true, "name": "session", "in": "path" - }, - { - "schema": { - "type": "string" - }, - "required": false, - "name": "phoneNumber", - "in": "query" - }, - { - "schema": { - "type": "string", - "pattern": "^[1-9][0-9]{0,18}$" - }, - "required": false, - "name": "id", - "in": "query" - }, - { - "schema": { - "type": "string", - "minLength": 3, - "maxLength": 35 - }, - "required": false, - "name": "username", - "in": "query" - }, - { - "schema": { - "type": "string", - "pattern": "^\\d{4}$" - }, - "required": false, - "name": "usernameKey", - "in": "query" } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BusinessCollectionReorderRequest" + } + } + } + }, "responses": { "200": { - "description": "Stable user ID and known aliases", + "description": "Collections reordered", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ResolveIdentityResponse" + "$ref": "#/components/schemas/ReorderBusinessCollectionsResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -35243,6 +43268,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -35264,6 +43300,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -35285,6 +43332,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -35306,6 +43364,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -35327,31 +43396,42 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API contacts:read" + "content": "Linked Device API profile:write" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `contacts:read`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" } }, - "/messaging/{session}/users/{id}/security-code": { - "get": { - "operationId": "getUserSecurityCode", + "/messaging/{session}/business/collections/{collectionId}/appeal": { + "post": { + "operationId": "appealBusinessCollection", "tags": [ - "Users" + "Business App" ], - "summary": "Get an identity verification code for a user", + "summary": "Request a review of a rejected WhatsApp Business App catalog collection", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "contacts:read", + "x-required-scope": "profile:write", "parameters": [ { "schema": { @@ -35364,22 +43444,43 @@ { "schema": { "type": "string", - "pattern": "^[1-9][0-9]{0,18}$" + "maxLength": 256 }, "required": true, - "name": "id", + "name": "collectionId", "in": "path" } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BusinessCatalogAppealRequest" + } + } + } + }, "responses": { "200": { - "description": "Identity verification code and display QR", + "description": "Collection appeal submitted", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UserSecurityCodeResponse" + "$ref": "#/components/schemas/AppealBusinessCollectionResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -35401,6 +43502,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -35422,6 +43534,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -35443,6 +43566,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -35464,6 +43598,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -35485,31 +43630,42 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API contacts:read" + "content": "Linked Device API profile:write" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `contacts:read`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" } }, - "/messaging/{session}/channels": { - "get": { - "operationId": "listChannels", + "/messaging/{session}/business/orders/{orderId}/lookup": { + "post": { + "operationId": "getBusinessOrder", "tags": [ - "Channels" + "Business App" ], - "summary": "List channels the session follows", + "summary": "Read WhatsApp Business App order details", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "channels:read", + "x-required-scope": "business:read", "parameters": [ { "schema": { @@ -35518,17 +43674,47 @@ "required": true, "name": "session", "in": "path" + }, + { + "schema": { + "type": "string", + "maxLength": 256 + }, + "required": true, + "name": "orderId", + "in": "path" } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BusinessOrderLookupRequest" + } + } + } + }, "responses": { "200": { - "description": "Channel list", + "description": "Order details", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ListChannelsResponse" + "$ref": "#/components/schemas/GetBusinessOrderResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -35550,6 +43736,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -35571,6 +43768,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -35592,6 +43800,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -35613,6 +43832,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -35634,29 +43864,74 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "502": { + "description": "Upstream WhatsApp request failed or its response could not be mapped", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "upstream_error", + "code": "upstream_failure", + "message": "The messaging provider could not complete the request.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#upstream-failure" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API channels:read" + "content": "Linked Device API business:read" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `channels:read`" - }, - "post": { - "operationId": "createChannel", + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `business:read`" + } + }, + "/messaging/{session}/identities/resolve": { + "get": { + "operationId": "resolveIdentity", "tags": [ - "Channels" + "Identities" ], - "summary": "Create a new channel", + "summary": "Resolve a user alias to a stable user ID", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "channels:manage", + "x-required-scope": "contacts:read", "parameters": [ { "schema": { @@ -35665,26 +43940,64 @@ "required": true, "name": "session", "in": "path" + }, + { + "schema": { + "type": "string" + }, + "required": false, + "name": "phoneNumber", + "in": "query" + }, + { + "schema": { + "type": "string", + "pattern": "^[1-9][0-9]{0,18}$" + }, + "required": false, + "name": "id", + "in": "query" + }, + { + "schema": { + "type": "string", + "minLength": 3, + "maxLength": 35 + }, + "required": false, + "name": "username", + "in": "query" + }, + { + "schema": { + "type": "string", + "pattern": "^\\d{4}$" + }, + "required": false, + "name": "usernameKey", + "in": "query" } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateChannelRequest" - } - } - } - }, "responses": { "200": { - "description": "Channel created", + "description": "Stable user ID and known aliases", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateChannelResponse" + "$ref": "#/components/schemas/ResolveIdentityResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -35706,6 +44019,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -35727,6 +44051,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -35748,6 +44083,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -35769,6 +44115,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -35790,31 +44147,42 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API channels:manage" + "content": "Linked Device API contacts:read" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `channels:manage`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `contacts:read`" } }, - "/messaging/{session}/channels/{id}": { + "/messaging/{session}/users/{id}/security-code": { "get": { - "operationId": "getChannel", + "operationId": "getUserSecurityCode", "tags": [ - "Channels" + "Users" ], - "summary": "Get channel details", + "summary": "Get an identity verification code for a user", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "channels:read", + "x-required-scope": "contacts:read", "parameters": [ { "schema": { @@ -35836,13 +44204,24 @@ ], "responses": { "200": { - "description": "Channel details", + "description": "Identity verification code and display QR", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetChannelResponse" + "$ref": "#/components/schemas/UserSecurityCodeResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -35864,6 +44243,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -35885,6 +44275,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -35906,6 +44307,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -35927,6 +44339,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -35948,29 +44371,42 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API channels:read" + "content": "Linked Device API contacts:read" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `channels:read`" - }, - "delete": { - "operationId": "deleteChannel", + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `contacts:read`" + } + }, + "/messaging/{session}/channels": { + "get": { + "operationId": "listChannels", "tags": [ "Channels" ], - "summary": "Delete a channel", + "summary": "List channels the session follows", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "channels:manage", + "x-required-scope": "channels:read", "parameters": [ { "schema": { @@ -35979,26 +44415,28 @@ "required": true, "name": "session", "in": "path" - }, - { - "schema": { - "type": "string", - "pattern": "^[1-9][0-9]{0,18}$" - }, - "required": true, - "name": "id", - "in": "path" } ], "responses": { "200": { - "description": "Channel deleted", + "description": "Channel list", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DeleteChannelResponse" + "$ref": "#/components/schemas/ListChannelsResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -36020,6 +44458,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -36041,6 +44490,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -36062,6 +44522,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -36083,6 +44554,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -36104,52 +44586,40 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } - } - }, - "502": { - "description": "Upstream WhatsApp request failed or its response could not be mapped", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "upstream_error", - "code": "upstream_failure", - "message": "The messaging provider could not complete the request.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#upstream-failure" - } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } } }, "x-mint": { - "content": "Linked Device API channels:manage" + "content": "Linked Device API channels:read" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `channels:manage`" - } - }, - "/messaging/{session}/channels/{id}/messages": { - "get": { - "operationId": "listChannelMessages", + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `channels:read`" + }, + "post": { + "operationId": "createChannel", "tags": [ "Channels" ], - "summary": "List channel messages", + "summary": "Create a new channel", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "channels:read", + "x-required-scope": "channels:manage", "parameters": [ { "schema": { @@ -36158,47 +44628,37 @@ "required": true, "name": "session", "in": "path" - }, - { - "schema": { - "type": "string", - "pattern": "^[1-9][0-9]{0,18}$" - }, - "required": true, - "name": "id", - "in": "path" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 50 - }, - "required": false, - "name": "count", - "in": "query" - }, - { - "schema": { - "type": "integer", - "minimum": 0, - "exclusiveMinimum": true - }, - "required": false, - "name": "before", - "in": "query" } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateChannelRequest" + } + } + } + }, "responses": { "200": { - "description": "Channel messages", + "description": "Channel created", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ListChannelMessagesResponse" + "$ref": "#/components/schemas/CreateChannelResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -36220,6 +44680,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -36241,6 +44712,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -36262,6 +44744,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -36283,6 +44776,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -36304,25 +44808,36 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API channels:read" + "content": "Linked Device API channels:manage" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `channels:read`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `channels:manage`" } }, - "/messaging/{session}/channels/{id}/message-updates": { + "/messaging/{session}/channels/{id}": { "get": { - "operationId": "listChannelMessageUpdates", + "operationId": "getChannel", "tags": [ "Channels" ], - "summary": "List channel view and reaction updates", + "summary": "Get channel details", "security": [ { "BearerAuth": [] @@ -36346,48 +44861,28 @@ "required": true, "name": "id", "in": "path" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "default": 50 - }, - "required": false, - "name": "count", - "in": "query" - }, - { - "schema": { - "type": "integer", - "nullable": true, - "minimum": 0 - }, - "required": false, - "name": "since", - "in": "query" - }, - { - "schema": { - "type": "integer", - "minimum": 0, - "exclusiveMinimum": true - }, - "required": false, - "name": "after", - "in": "query" } ], "responses": { "200": { - "description": "Channel message updates", + "description": "Channel details", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ListChannelMessageUpdatesResponse" + "$ref": "#/components/schemas/GetChannelResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -36409,6 +44904,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -36430,6 +44936,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -36451,6 +44968,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -36472,6 +45000,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -36493,6 +45032,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -36503,15 +45053,13 @@ "Linked Device API" ], "description": "**Protocols:** Linked Device API\n\n**Required scope:** `channels:read`" - } - }, - "/messaging/{session}/channels/{id}/messages/{messageId}/viewed": { - "post": { - "operationId": "markChannelMessageViewed", + }, + "delete": { + "operationId": "deleteChannel", "tags": [ "Channels" ], - "summary": "Mark a channel message as viewed", + "summary": "Delete a channel", "security": [ { "BearerAuth": [] @@ -36535,26 +45083,28 @@ "required": true, "name": "id", "in": "path" - }, - { - "schema": { - "type": "string", - "pattern": "^[1-9][0-9]{0,18}$" - }, - "required": true, - "name": "messageId", - "in": "path" } ], "responses": { "200": { - "description": "Message marked viewed", + "description": "Channel deleted", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/DeleteChannelResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -36576,6 +45126,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -36597,6 +45158,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -36618,6 +45190,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -36639,6 +45222,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -36660,6 +45254,49 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "502": { + "description": "Upstream WhatsApp request failed or its response could not be mapped", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "upstream_error", + "code": "upstream_failure", + "message": "The messaging provider could not complete the request.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#upstream-failure" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -36672,19 +45309,19 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `channels:manage`" } }, - "/messaging/{session}/channels/{id}/messages/{messageId}/reaction": { - "post": { - "operationId": "reactToChannelMessage", + "/messaging/{session}/channels/{id}/messages": { + "get": { + "operationId": "listChannelMessages", "tags": [ "Channels" ], - "summary": "React to a channel message", + "summary": "List channel messages", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "channels:manage", + "x-required-scope": "channels:read", "parameters": [ { "schema": { @@ -36705,43 +45342,46 @@ }, { "schema": { - "type": "string", - "pattern": "^[1-9][0-9]{0,18}$" + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 50 }, - "required": true, - "name": "messageId", - "in": "path" + "required": false, + "name": "count", + "in": "query" }, { - "name": "Idempotency-Key", - "in": "header", - "required": false, - "description": "Unique key, 1 to 255 bytes of UTF-8, that makes this write safe to retry for 24 hours. A retry with the same key, credential, `Polymorfa-Version`, and request never runs the write again: it returns `409 idempotency_completed` when the original succeeded, or the original server error, with `Idempotent-Replayed: true`. Reusing the key for a different request returns `409 idempotency_conflict`; a retry while the original is still running returns `409 idempotency_in_progress`, and a retry after an unrecorded outcome returns `409 idempotency_outcome_unknown`.", "schema": { - "type": "string", - "minLength": 1, - "maxLength": 255 - } + "type": "integer", + "minimum": 0, + "exclusiveMinimum": true + }, + "required": false, + "name": "before", + "in": "query" } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/ChannelReactionRequest" - } - } - } - }, "responses": { "200": { - "description": "Reaction updated", + "description": "Channel messages", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/ListChannelMessagesResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -36763,6 +45403,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -36784,6 +45435,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -36805,6 +45467,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -36826,43 +45499,16 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } - } - }, - "409": { - "description": "The Idempotency-Key already succeeded (`idempotency_completed`), was used for a different request (`idempotency_conflict`), belongs to a request that is still running (`idempotency_in_progress`, with Retry-After), or belongs to a request whose outcome was not recorded (`idempotency_outcome_unknown`)", + }, "headers": { - "Retry-After": { - "description": "Seconds to wait before retrying `idempotency_in_progress`.", - "schema": { - "type": "integer", - "minimum": 0 - } + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" }, - "Idempotent-Replayed": { - "description": "`true` when this response reports the recorded outcome of an earlier request with the same Idempotency-Key.", - "schema": { - "type": "string", - "enum": [ - "true" - ] - } - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "conflict_error", - "code": "idempotency_conflict", - "message": "This Idempotency-Key was already used with a different request. Use a new key for a different request.", - "param": null - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#idempotency-conflict" - } + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -36885,25 +45531,36 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API channels:manage" + "content": "Linked Device API channels:read" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `channels:manage`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `channels:read`" } }, - "/messaging/{session}/channels/{id}/live-updates": { - "post": { - "operationId": "subscribeChannelLiveUpdates", + "/messaging/{session}/channels/{id}/message-updates": { + "get": { + "operationId": "listChannelMessageUpdates", "tags": [ "Channels" ], - "summary": "Subscribe temporarily to channel live updates", + "summary": "List channel view and reaction updates", "security": [ { "BearerAuth": [] @@ -36927,17 +45584,59 @@ "required": true, "name": "id", "in": "path" + }, + { + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "default": 50 + }, + "required": false, + "name": "count", + "in": "query" + }, + { + "schema": { + "type": "integer", + "nullable": true, + "minimum": 0 + }, + "required": false, + "name": "since", + "in": "query" + }, + { + "schema": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": true + }, + "required": false, + "name": "after", + "in": "query" } ], "responses": { "200": { - "description": "Live-update subscription duration", + "description": "Channel message updates", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ChannelLiveUpdatesResponse" + "$ref": "#/components/schemas/ListChannelMessageUpdatesResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -36959,6 +45658,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -36980,6 +45690,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -37001,6 +45722,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -37022,6 +45754,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -37043,6 +45786,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -37055,13 +45809,13 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `channels:read`" } }, - "/messaging/{session}/channels/{id}/follow": { + "/messaging/{session}/channels/{id}/messages/{messageId}/viewed": { "post": { - "operationId": "followChannel", + "operationId": "markChannelMessageViewed", "tags": [ "Channels" ], - "summary": "Follow a channel", + "summary": "Mark a channel message as viewed", "security": [ { "BearerAuth": [] @@ -37085,17 +45839,37 @@ "required": true, "name": "id", "in": "path" + }, + { + "schema": { + "type": "string", + "pattern": "^[1-9][0-9]{0,18}$" + }, + "required": true, + "name": "messageId", + "in": "path" } ], "responses": { "200": { - "description": "Channel followed", + "description": "Message marked viewed", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -37117,6 +45891,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -37138,6 +45923,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -37159,6 +45955,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -37180,6 +45987,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -37201,6 +46019,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -37213,13 +46042,13 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `channels:manage`" } }, - "/messaging/{session}/channels/{id}/unfollow": { + "/messaging/{session}/channels/{id}/messages/{messageId}/reaction": { "post": { - "operationId": "unfollowChannel", + "operationId": "reactToChannelMessage", "tags": [ "Channels" ], - "summary": "Unfollow a channel", + "summary": "React to a channel message", "security": [ { "BearerAuth": [] @@ -37243,17 +46072,57 @@ "required": true, "name": "id", "in": "path" + }, + { + "schema": { + "type": "string", + "pattern": "^[1-9][0-9]{0,18}$" + }, + "required": true, + "name": "messageId", + "in": "path" + }, + { + "name": "Idempotency-Key", + "in": "header", + "required": false, + "description": "Unique key, 1 to 255 bytes of UTF-8, that makes this write safe to retry for 24 hours. A retry with the same key, credential, `Polymorfa-Version`, and request never runs the write again: it returns `409 idempotency_completed` when the original succeeded, or the original server error, with `Idempotent-Replayed: true`. Reusing the key for a different request returns `409 idempotency_conflict`; a retry while the original is still running returns `409 idempotency_in_progress`, and a retry after an unrecorded outcome returns `409 idempotency_outcome_unknown`.", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 255 + } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ChannelReactionRequest" + } + } + } + }, "responses": { "200": { - "description": "Channel unfollowed", + "description": "Reaction updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -37275,6 +46144,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -37296,6 +46176,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -37317,6 +46208,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -37338,6 +46240,64 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "409": { + "description": "The Idempotency-Key already succeeded (`idempotency_completed`), was used for a different request (`idempotency_conflict`), belongs to a request that is still running (`idempotency_in_progress`, with Retry-After), or belongs to a request whose outcome was not recorded (`idempotency_outcome_unknown`)", + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying `idempotency_in_progress`.", + "schema": { + "type": "integer", + "minimum": 0 + } + }, + "Idempotent-Replayed": { + "description": "`true` when this response reports the recorded outcome of an earlier request with the same Idempotency-Key.", + "schema": { + "type": "string", + "enum": [ + "true" + ] + } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "conflict_error", + "code": "idempotency_conflict", + "message": "This Idempotency-Key was already used with a different request. Use a new key for a different request.", + "param": null + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#idempotency-conflict" + } + } } }, "500": { @@ -37359,6 +46319,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -37371,19 +46342,19 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `channels:manage`" } }, - "/messaging/{session}/channels/{id}/mute": { + "/messaging/{session}/channels/{id}/live-updates": { "post": { - "operationId": "muteChannel", + "operationId": "subscribeChannelLiveUpdates", "tags": [ "Channels" ], - "summary": "Mute a channel", + "summary": "Subscribe temporarily to channel live updates", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "channels:manage", + "x-required-scope": "channels:read", "parameters": [ { "schema": { @@ -37405,13 +46376,24 @@ ], "responses": { "200": { - "description": "Channel muted", + "description": "Live-update subscription duration", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/ChannelLiveUpdatesResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -37433,6 +46415,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -37454,6 +46447,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -37475,6 +46479,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -37496,6 +46511,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -37517,25 +46543,36 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API channels:manage" + "content": "Linked Device API channels:read" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `channels:manage`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `channels:read`" } }, - "/messaging/{session}/channels/{id}/unmute": { + "/messaging/{session}/channels/{id}/follow": { "post": { - "operationId": "unmuteChannel", + "operationId": "followChannel", "tags": [ "Channels" ], - "summary": "Unmute a channel", + "summary": "Follow a channel", "security": [ { "BearerAuth": [] @@ -37563,13 +46600,24 @@ ], "responses": { "200": { - "description": "Channel unmuted", + "description": "Channel followed", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -37591,6 +46639,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -37612,6 +46671,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -37633,6 +46703,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -37654,6 +46735,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -37675,6 +46767,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -37687,19 +46790,19 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `channels:manage`" } }, - "/messaging/{session}/presence": { + "/messaging/{session}/channels/{id}/unfollow": { "post": { - "operationId": "setPresence", + "operationId": "unfollowChannel", "tags": [ - "Presence" + "Channels" ], - "summary": "Set the session's own presence (available / unavailable)", + "summary": "Unfollow a channel", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "presence:write", + "x-required-scope": "channels:manage", "parameters": [ { "schema": { @@ -37708,35 +46811,36 @@ "required": true, "name": "session", "in": "path" + }, + { + "schema": { + "type": "string", + "pattern": "^[1-9][0-9]{0,18}$" + }, + "required": true, + "name": "id", + "in": "path" } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SetPresenceRequest" - } - } - } - }, "responses": { "200": { - "description": "Presence updated", + "description": "Channel unfollowed", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuccessResponse" } } - } - }, - "202": { - "description": "Accepted for asynchronous processing in response to `Prefer: respond-async`; the result is not included", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/AsyncAcceptedResponse" - } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -37759,6 +46863,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -37780,6 +46895,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -37801,6 +46927,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -37822,6 +46959,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -37843,29 +46991,42 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API presence:write" + "content": "Linked Device API channels:manage" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `presence:write`" - }, - "get": { - "operationId": "getPresence", + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `channels:manage`" + } + }, + "/messaging/{session}/channels/{id}/mute": { + "post": { + "operationId": "muteChannel", "tags": [ - "Presence" + "Channels" ], - "summary": "Get the session's own presence", + "summary": "Mute a channel", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "presence:read", + "x-required-scope": "channels:manage", "parameters": [ { "schema": { @@ -37874,17 +47035,37 @@ "required": true, "name": "session", "in": "path" + }, + { + "schema": { + "type": "string", + "pattern": "^[1-9][0-9]{0,18}$" + }, + "required": true, + "name": "id", + "in": "path" } ], "responses": { "200": { - "description": "Presence info", + "description": "Channel muted", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetPresenceResponse" + "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -37906,6 +47087,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -37927,6 +47119,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -37948,6 +47151,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -37969,6 +47183,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -37990,31 +47215,42 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API presence:read" + "content": "Linked Device API channels:manage" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `presence:read`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `channels:manage`" } }, - "/messaging/{session}/presence/{conversation}": { - "get": { - "operationId": "getChatPresence", + "/messaging/{session}/channels/{id}/unmute": { + "post": { + "operationId": "unmuteChannel", "tags": [ - "Presence" + "Channels" ], - "summary": "Get presence for a specific chat", + "summary": "Unmute a channel", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "presence:read", + "x-required-scope": "channels:manage", "parameters": [ { "schema": { @@ -38026,22 +47262,34 @@ }, { "schema": { - "type": "string" + "type": "string", + "pattern": "^[1-9][0-9]{0,18}$" }, "required": true, - "name": "conversation", + "name": "id", "in": "path" } ], "responses": { "200": { - "description": "Chat presence", + "description": "Channel unmuted", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetChatPresenceResponse" + "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -38063,6 +47311,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -38084,6 +47343,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -38105,6 +47375,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -38126,6 +47407,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -38147,31 +47439,42 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API presence:read" + "content": "Linked Device API channels:manage" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `presence:read`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `channels:manage`" } }, - "/messaging/{session}/presence/{conversation}/subscribe": { + "/messaging/{session}/presence": { "post": { - "operationId": "subscribePresence", + "operationId": "setPresence", "tags": [ "Presence" ], - "summary": "Subscribe to presence updates for a chat", + "summary": "Set the session's own presence (available / unavailable)", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "presence:observe", + "x-required-scope": "presence:write", "parameters": [ { "schema": { @@ -38180,25 +47483,37 @@ "required": true, "name": "session", "in": "path" - }, - { - "schema": { - "type": "string" - }, - "required": true, - "name": "conversation", - "in": "path" } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SetPresenceRequest" + } + } + } + }, "responses": { "200": { - "description": "Subscribed", + "description": "Presence updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SubscribePresenceResponse" + "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "202": { @@ -38209,6 +47524,17 @@ "$ref": "#/components/schemas/AsyncAcceptedResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -38230,6 +47556,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -38251,6 +47588,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -38272,6 +47620,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -38293,26 +47652,16 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } - } - }, - "429": { - "description": "Observation limit exceeded, or the session is inside the suspension window that follows", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "rate_limit_error", - "code": "rate_limit_exceeded", - "message": "The request limit was reached. Follow Retry-After when supplied.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#rate-limit-exceeded" - } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -38335,51 +47684,70 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API presence:observe" + "content": "Linked Device API presence:write" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `presence:observe`" - } - }, - "/messaging/projects/{projectId}/safe-mode": { + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `presence:write`" + }, "get": { - "operationId": "getProjectSafeMode", + "operationId": "getPresence", "tags": [ - "Safe Mode" + "Presence" ], - "summary": "Get a project's Safe Mode ceiling", + "summary": "Get the session's own presence", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "sessions:read", + "x-required-scope": "presence:read", "parameters": [ { "schema": { - "type": "string", - "format": "uuid" + "type": "string" }, "required": true, - "name": "projectId", + "name": "session", "in": "path" } ], "responses": { "200": { - "description": "Project Safe Mode ceiling, and whether the plan includes BanSafe Lite", + "description": "Presence info", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetProjectSafeModeResponse" + "$ref": "#/components/schemas/GetPresenceResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -38401,6 +47769,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -38422,6 +47801,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -38443,6 +47833,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -38464,6 +47865,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -38485,101 +47897,80 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "sessions:read" + "content": "Linked Device API presence:read" }, - "description": "**Required scope:** `sessions:read`" - }, - "put": { - "operationId": "updateProjectSafeMode", + "x-protocols": [ + "Linked Device API" + ], + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `presence:read`" + } + }, + "/messaging/{session}/presence/{conversation}": { + "get": { + "operationId": "getChatPresence", "tags": [ - "Safe Mode" + "Presence" ], - "summary": "Set a project's Safe Mode ceiling", - "description": "**Required scope:** `sessions:manage`\n\nSets the most visible behaviour any number in this project may use. Presence and read receipts are visible to the people this number messages: turning them on makes the number appear online, exposes last-seen subject to the account's privacy settings, and puts blue ticks on incoming messages. Every axis is off by default and only this request changes it.", + "summary": "Get presence for a specific chat", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "sessions:manage", + "x-required-scope": "presence:read", "parameters": [ { "schema": { - "type": "string", - "format": "uuid" + "type": "string" }, "required": true, - "name": "projectId", + "name": "session", + "in": "path" + }, + { + "schema": { + "type": "string" + }, + "required": true, + "name": "conversation", "in": "path" } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "presence": { - "type": "string", - "enum": [ - "dark", - "online_while_sending", - "online_hours" - ] - }, - "typing": { - "type": "string", - "enum": [ - "off", - "before_text", - "before_all" - ] - }, - "reads": { - "type": "string", - "enum": [ - "off", - "replied_chats", - "all_inbound" - ] - }, - "pacing": { - "type": "string", - "enum": [ - "off", - "jittered", - "conversation" - ] - }, - "onlineStart": { - "type": "integer", - "minimum": 0, - "maximum": 23 - }, - "onlineEnd": { - "type": "integer", - "minimum": 0, - "maximum": 23 - } - }, - "additionalProperties": false - } - } - } - }, "responses": { "200": { - "description": "Project Safe Mode ceiling updated", + "description": "Chat presence", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateProjectSafeModeResponse" + "$ref": "#/components/schemas/GetChatPresenceResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -38601,6 +47992,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -38622,26 +48024,16 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } - } - }, - "402": { - "description": "Safe Mode is part of BanSafe Lite, which is not included on this number's plan", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "permission_error", - "code": "entitlement_required", - "message": "The account needs the required entitlement for this action.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#entitlement-required" - } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -38664,6 +48056,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -38685,6 +48088,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -38706,68 +48120,101 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } - } - }, - "503": { - "description": "Stored, but a connected number did not confirm the policy; retry the request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "api_error", - "code": "service_unavailable", - "message": "A required service is unavailable. Check completion before repeating a write.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" - } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } } }, "x-mint": { - "content": "sessions:manage" - } + "content": "Linked Device API presence:read" + }, + "x-protocols": [ + "Linked Device API" + ], + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `presence:read`" } }, - "/messaging/{session}/safe-mode": { - "get": { - "operationId": "getSessionSafeMode", + "/messaging/{session}/presence/{conversation}/subscribe": { + "post": { + "operationId": "subscribePresence", "tags": [ - "Safe Mode" + "Presence" ], - "summary": "Get a number's effective Safe Mode", + "summary": "Subscribe to presence updates for a chat", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "sessions:read", + "x-required-scope": "presence:observe", "parameters": [ { "schema": { - "type": "string", - "minLength": 1 + "type": "string" }, "required": true, "name": "session", "in": "path" + }, + { + "schema": { + "type": "string" + }, + "required": true, + "name": "conversation", + "in": "path" } ], "responses": { "200": { - "description": "Project ceiling, number override, the effective policy, and whether the plan includes BanSafe Lite", + "description": "Subscribed", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetSessionSafeModeResponse" + "$ref": "#/components/schemas/SubscribePresenceResponse" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "202": { + "description": "Accepted for asynchronous processing in response to `Prefer: respond-async`; the result is not included", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AsyncAcceptedResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -38789,6 +48236,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -38810,6 +48268,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -38831,6 +48300,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -38852,6 +48332,49 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "429": { + "description": "Observation limit exceeded, or the session is inside the suspension window that follows", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "rate_limit_error", + "code": "rate_limit_exceeded", + "message": "The request limit was reached. Follow Retry-After when supplied.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#rate-limit-exceeded" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -38873,98 +48396,73 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API sessions:read" + "content": "Linked Device API presence:observe" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `sessions:read`" - }, - "put": { - "operationId": "updateSessionSafeMode", + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `presence:observe`" + } + }, + "/messaging/projects/{projectId}/safe-mode": { + "get": { + "operationId": "getProjectSafeMode", "tags": [ "Safe Mode" ], - "summary": "Set a number's Safe Mode override", - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `sessions:manage`\n\nSets one number's Safe Mode. Each axis accepts `inherit` to follow the project ceiling. An override may only be equally or more conservative than the ceiling; a more visible value is refused with `safe_mode_exceeds_project_ceiling`.", + "summary": "Get a project's Safe Mode ceiling", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "sessions:manage", + "x-required-scope": "sessions:read", "parameters": [ { "schema": { "type": "string", - "minLength": 1 + "format": "uuid" }, "required": true, - "name": "session", + "name": "projectId", "in": "path" } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "presence": { - "type": "string", - "enum": [ - "inherit", - "dark", - "online_while_sending", - "online_hours" - ] - }, - "typing": { - "type": "string", - "enum": [ - "inherit", - "off", - "before_text", - "before_all" - ] - }, - "reads": { - "type": "string", - "enum": [ - "inherit", - "off", - "replied_chats", - "all_inbound" - ] - }, - "pacing": { - "type": "string", - "enum": [ - "inherit", - "off", - "jittered", - "conversation" - ] - } - }, - "additionalProperties": false - } - } - } - }, "responses": { "200": { - "description": "Number Safe Mode override updated", + "description": "Project Safe Mode ceiling, and whether the plan includes BanSafe Lite", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateSessionSafeModeResponse" + "$ref": "#/components/schemas/GetProjectSafeModeResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -38986,6 +48484,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -39007,26 +48516,16 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } - } - }, - "402": { - "description": "Safe Mode is part of BanSafe Lite, which is not included on this number's plan", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "permission_error", - "code": "entitlement_required", - "message": "The account needs the required entitlement for this action.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#entitlement-required" - } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -39049,6 +48548,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -39070,6 +48580,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -39091,191 +48612,32 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } - } - }, - "503": { - "description": "Stored, but a connected number did not confirm the policy; retry the request", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "api_error", - "code": "service_unavailable", - "message": "A required service is unavailable. Check completion before repeating a write.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" - } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } } }, "x-mint": { - "content": "Linked Device API sessions:manage" - }, - "x-protocols": [ - "Linked Device API" - ] - } - }, - "/messaging/projects/{projectId}/warmup-plan": { - "get": { - "operationId": "getProjectWarmupPlan", - "tags": [ - "Warm-up plan" - ], - "summary": "Get a project's warm-up plan", - "security": [ - { - "BearerAuth": [] - } - ], - "x-required-scope": "sessions:read", - "parameters": [ - { - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true, - "name": "projectId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "Project warm-up plan, its projected curve, and whether the plan includes BanSafe Lite", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/GetProjectWarmupPlanResponse" - } - } - } - }, - "400": { - "description": "Bad request — request body or params failed validation", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "invalid_request_error", - "code": "invalid_parameter", - "message": "Correct the invalid request field.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" - } - } - } - }, - "401": { - "description": "Missing or invalid authentication token", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "authentication_error", - "code": "invalid_credential", - "message": "Supply a valid credential.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" - } - } - } - }, - "403": { - "description": "Authenticated but not permitted (missing scope or rule violation)", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "permission_error", - "code": "permission_denied", - "message": "The credential does not have access to this resource.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#permission-denied" - } - } - } - }, - "404": { - "description": "Resource not found", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "invalid_request_error", - "code": "resource_not_found", - "message": "The resource is unavailable to this caller.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" - } - } - } - }, - "500": { - "description": "Internal server error", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "api_error", - "code": "internal_error", - "message": "An unexpected service failure occurred. Keep the request ID for support.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#internal-error" - } - } - } - } - }, - "x-mint": { - "content": "sessions:read" + "content": "sessions:read" }, "description": "**Required scope:** `sessions:read`" }, "put": { - "operationId": "updateProjectWarmupPlan", + "operationId": "updateProjectSafeMode", "tags": [ - "Warm-up plan" + "Safe Mode" ], - "summary": "Set a project's warm-up plan", - "description": "**Required scope:** `sessions:manage`\n\nTurns the warm-up plan on or off for a project and sets how long the ramp takes and how many messages a number may send on its first day. While the plan is on, a send past a number's allowance is refused with `bansafe_daily_allowance_reached` and campaign recipients are deferred to the next reset.", + "summary": "Set a project's Safe Mode ceiling", + "description": "**Required scope:** `sessions:manage`\n\nSets the most visible behaviour any number in this project may use. Presence and read receipts are visible to the people this number messages: turning them on makes the number appear online, exposes last-seen subject to the account's privacy settings, and puts blue ticks on incoming messages. Every axis is off by default and only this request changes it.", "security": [ { "BearerAuth": [] @@ -39299,18 +48661,47 @@ "schema": { "type": "object", "properties": { - "enabled": { - "type": "boolean" + "presence": { + "type": "string", + "enum": [ + "dark", + "online_while_sending", + "online_hours" + ] }, - "warmupDays": { + "typing": { + "type": "string", + "enum": [ + "off", + "before_text", + "before_all" + ] + }, + "reads": { + "type": "string", + "enum": [ + "off", + "replied_chats", + "all_inbound" + ] + }, + "pacing": { + "type": "string", + "enum": [ + "off", + "jittered", + "conversation" + ] + }, + "onlineStart": { "type": "integer", - "minimum": 1, - "maximum": 90 + "minimum": 0, + "maximum": 23 }, - "dailyStart": { + "onlineEnd": { "type": "integer", - "minimum": 1, - "maximum": 2000 + "minimum": 0, + "maximum": 23 } }, "additionalProperties": false @@ -39320,13 +48711,24 @@ }, "responses": { "200": { - "description": "Project warm-up plan updated", + "description": "Project Safe Mode ceiling updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateProjectWarmupPlanResponse" + "$ref": "#/components/schemas/UpdateProjectSafeModeResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -39348,6 +48750,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -39369,10 +48782,21 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "402": { - "description": "The warm-up plan is part of BanSafe Lite, which is not included on this project's plan", + "description": "Safe Mode is part of BanSafe Lite, which is not included on this number's plan", "content": { "application/json": { "schema": { @@ -39390,6 +48814,17 @@ "docs": "https://docs.polymorfa.com/api/errors#entitlement-required" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -39411,6 +48846,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -39432,6 +48878,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -39453,10 +48910,21 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { - "description": "Stored, but a connected number did not confirm the plan; retry the request", + "description": "Stored, but a connected number did not confirm the policy; retry the request", "content": { "application/json": { "schema": { @@ -39474,6 +48942,17 @@ "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -39482,13 +48961,13 @@ } } }, - "/messaging/projects/{projectId}/health-policy": { + "/messaging/{session}/safe-mode": { "get": { - "operationId": "getProjectHealthPolicy", + "operationId": "getSessionSafeMode", "tags": [ - "BanSafe" + "Safe Mode" ], - "summary": "Get a project's health policy", + "summary": "Get a number's effective Safe Mode", "security": [ { "BearerAuth": [] @@ -39499,22 +48978,33 @@ { "schema": { "type": "string", - "format": "uuid" + "minLength": 1 }, "required": true, - "name": "projectId", + "name": "session", "in": "path" } ], "responses": { "200": { - "description": "Project health threshold and configured actions", + "description": "Project ceiling, number override, the effective policy, and whether the plan includes BanSafe Lite", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetProjectHealthPolicyResponse" + "$ref": "#/components/schemas/GetSessionSafeModeResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -39536,6 +49026,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -39557,6 +49058,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -39578,6 +49090,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -39599,6 +49122,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -39620,21 +49154,35 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "sessions:read" + "content": "Linked Device API sessions:read" }, - "description": "**Required scope:** `sessions:read`" + "x-protocols": [ + "Linked Device API" + ], + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `sessions:read`" }, "put": { - "operationId": "updateProjectHealthPolicy", + "operationId": "updateSessionSafeMode", "tags": [ - "BanSafe" + "Safe Mode" ], - "summary": "Set a project's health policy", - "description": "**Required scope:** `sessions:manage`\n\nReplaces the project policy when version matches the stored version. The rule triggers only when an available health estimate is below threshold. Stopping disconnects a session while retaining its linked credentials; logging out requires relinking.", + "summary": "Set a number's Safe Mode override", + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `sessions:manage`\n\nSets one number's Safe Mode. Each axis accepts `inherit` to follow the project ceiling. An override may only be equally or more conservative than the ceiling; a more visible value is refused with `safe_mode_exceeds_project_ceiling`.", "security": [ { "BearerAuth": [] @@ -39645,32 +49193,81 @@ { "schema": { "type": "string", - "format": "uuid" + "minLength": 1 }, "required": true, - "name": "projectId", + "name": "session", "in": "path" } ], "requestBody": { - "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateProjectHealthPolicyInput" + "type": "object", + "properties": { + "presence": { + "type": "string", + "enum": [ + "inherit", + "dark", + "online_while_sending", + "online_hours" + ] + }, + "typing": { + "type": "string", + "enum": [ + "inherit", + "off", + "before_text", + "before_all" + ] + }, + "reads": { + "type": "string", + "enum": [ + "inherit", + "off", + "replied_chats", + "all_inbound" + ] + }, + "pacing": { + "type": "string", + "enum": [ + "inherit", + "off", + "jittered", + "conversation" + ] + } + }, + "additionalProperties": false } } } }, "responses": { "200": { - "description": "Project health policy updated", + "description": "Number Safe Mode override updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateProjectHealthPolicyResponse" + "$ref": "#/components/schemas/UpdateSessionSafeModeResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -39692,6 +49289,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -39713,6 +49321,49 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "402": { + "description": "Safe Mode is part of BanSafe Lite, which is not included on this number's plan", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "permission_error", + "code": "entitlement_required", + "message": "The account needs the required entitlement for this action.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#entitlement-required" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -39734,6 +49385,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -39755,10 +49417,21 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, - "409": { - "description": "Version conflict or selected notification integration unavailable", + "500": { + "description": "Internal server error", "content": { "application/json": { "schema": { @@ -39766,20 +49439,31 @@ }, "example": { "error": { - "type": "conflict_error", - "code": "state_conflict", - "message": "The request conflicts with the resource state.", + "type": "api_error", + "code": "internal_error", + "message": "An unexpected service failure occurred. Keep the request ID for support.", "param": null, "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" }, "data": null, - "docs": "https://docs.polymorfa.com/api/errors#state-conflict" + "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, - "500": { - "description": "Internal server error", + "503": { + "description": "Stored, but a connected number did not confirm the policy; retry the request", "content": { "application/json": { "schema": { @@ -39788,31 +49472,44 @@ "example": { "error": { "type": "api_error", - "code": "internal_error", - "message": "An unexpected service failure occurred. Keep the request ID for support.", + "code": "service_unavailable", + "message": "A required service is unavailable. Check completion before repeating a write.", "param": null, "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" }, "data": null, - "docs": "https://docs.polymorfa.com/api/errors#internal-error" + "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "sessions:manage" - } + "content": "Linked Device API sessions:manage" + }, + "x-protocols": [ + "Linked Device API" + ] } }, - "/messaging/projects/{projectId}/insurance-evidence": { + "/messaging/projects/{projectId}/warmup-plan": { "get": { - "operationId": "getProjectInsuranceEvidence", + "operationId": "getProjectWarmupPlan", "tags": [ - "Ban insurance" + "Warm-up plan" ], - "summary": "Get ban insurance evidence setting", - "description": "**Required scope:** `sessions:read`\n\nBan insurance evidence lets Polymorfa measure what the other devices linked to a number do with it: how many messages they send, to how many people, how fast, and in which hours. Only counts are kept, never messages, contacts, or device names. With it off, a claim can still be filed, but Polymorfa cannot say whether another device caused the ban and a person decides the claim.", + "summary": "Get a project's warm-up plan", "security": [ { "BearerAuth": [] @@ -39832,13 +49529,24 @@ ], "responses": { "200": { - "description": "The project's ban insurance evidence setting", + "description": "Project warm-up plan, its projected curve, and whether the plan includes BanSafe Lite", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetProjectInsuranceEvidenceResponse" + "$ref": "#/components/schemas/GetProjectWarmupPlanResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -39860,6 +49568,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -39881,6 +49600,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -39902,6 +49632,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -39923,6 +49664,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -39944,20 +49696,32 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { "content": "sessions:read" - } + }, + "description": "**Required scope:** `sessions:read`" }, "put": { - "operationId": "updateProjectInsuranceEvidence", + "operationId": "updateProjectWarmupPlan", "tags": [ - "Ban insurance" + "Warm-up plan" ], - "summary": "Set ban insurance evidence setting", - "description": "**Required scope:** `sessions:manage`\n\nBan insurance evidence lets Polymorfa measure what the other devices linked to a number do with it: how many messages they send, to how many people, how fast, and in which hours. Only counts are kept, never messages, contacts, or device names. With it off, a claim can still be filed, but Polymorfa cannot say whether another device caused the ban and a person decides the claim.", + "summary": "Set a project's warm-up plan", + "description": "**Required scope:** `sessions:manage`\n\nTurns the warm-up plan on or off for a project and sets how long the ramp takes and how many messages a number may send on its first day. While the plan is on, a send past a number's allowance is refused with `bansafe_daily_allowance_reached` and campaign recipients are deferred to the next reset.", "security": [ { "BearerAuth": [] @@ -39983,11 +49747,18 @@ "properties": { "enabled": { "type": "boolean" + }, + "warmupDays": { + "type": "integer", + "minimum": 1, + "maximum": 90 + }, + "dailyStart": { + "type": "integer", + "minimum": 1, + "maximum": 2000 } }, - "required": [ - "enabled" - ], "additionalProperties": false } } @@ -39995,13 +49766,24 @@ }, "responses": { "200": { - "description": "The project's ban insurance evidence setting was updated", + "description": "Project warm-up plan updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateProjectInsuranceEvidenceResponse" + "$ref": "#/components/schemas/UpdateProjectWarmupPlanResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -40023,6 +49805,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -40044,6 +49837,49 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "402": { + "description": "The warm-up plan is part of BanSafe Lite, which is not included on this project's plan", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "permission_error", + "code": "entitlement_required", + "message": "The account needs the required entitlement for this action.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#entitlement-required" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -40065,6 +49901,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -40086,6 +49933,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -40107,10 +49965,21 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { - "description": "Stored, but a connected number did not confirm the change; retry the request", + "description": "Stored, but a connected number did not confirm the plan; retry the request", "content": { "application/json": { "schema": { @@ -40128,6 +49997,17 @@ "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -40136,20 +50016,19 @@ } } }, - "/messaging/projects/{projectId}/observation-policy": { + "/messaging/projects/{projectId}/health-policy": { "get": { - "operationId": "getProjectObservationPolicy", + "operationId": "getProjectHealthPolicy", "tags": [ - "Presence", - "Labels", - "Business App" + "BanSafe" ], + "summary": "Get a project's health policy", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "presence:read", + "x-required-scope": "sessions:read", "parameters": [ { "schema": { @@ -40163,13 +50042,24 @@ ], "responses": { "200": { - "description": "Project observation ceiling", + "description": "Project health threshold and configured actions", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetProjectObservationPolicyResponse" + "$ref": "#/components/schemas/GetProjectHealthPolicyResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -40191,6 +50081,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -40212,6 +50113,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -40233,6 +50145,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -40254,6 +50177,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -40275,49 +50209,79 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "presence:read" + "content": "sessions:read" }, - "description": "**Required scope:** `presence:read`" - } - }, - "/messaging/{session}/observation-policy": { - "get": { - "operationId": "getSessionObservationPolicy", + "description": "**Required scope:** `sessions:read`" + }, + "put": { + "operationId": "updateProjectHealthPolicy", "tags": [ - "Presence", - "Labels", - "Business App" + "BanSafe" ], + "summary": "Set a project's health policy", + "description": "**Required scope:** `sessions:manage`\n\nReplaces the project policy when version matches the stored version. The rule triggers only when an available health estimate is below threshold. Stopping disconnects a session while retaining its linked credentials; logging out requires relinking.", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "presence:read", + "x-required-scope": "sessions:manage", "parameters": [ { "schema": { "type": "string", - "minLength": 1 + "format": "uuid" }, "required": true, - "name": "session", + "name": "projectId", "in": "path" } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateProjectHealthPolicyInput" + } + } + } + }, "responses": { "200": { - "description": "Effective session observation policy", + "description": "Project health policy updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetSessionObservationPolicyResponse" + "$ref": "#/components/schemas/UpdateProjectHealthPolicyResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -40339,6 +50303,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -40360,6 +50335,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -40381,6 +50367,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -40402,6 +50399,49 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "409": { + "description": "Version conflict or selected notification integration unavailable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "conflict_error", + "code": "state_conflict", + "message": "The request conflicts with the resource state.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#state-conflict" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -40423,50 +50463,70 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API presence:read" - }, - "x-protocols": [ - "Linked Device API" - ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `presence:read`" + "content": "sessions:manage" + } } }, - "/messaging/{session}/profile": { + "/messaging/projects/{projectId}/insurance-evidence": { "get": { - "operationId": "getProfile", + "operationId": "getProjectInsuranceEvidence", "tags": [ - "Profile" + "Ban insurance" ], - "summary": "Get the session's own profile", + "summary": "Get ban insurance evidence setting", + "description": "**Required scope:** `sessions:read`\n\nBan insurance evidence lets Polymorfa measure what the other devices linked to a number do with it: how many messages they send, to how many people, how fast, and in which hours. Only counts are kept, never messages, contacts, or device names. With it off, a claim can still be filed, but Polymorfa cannot say whether another device caused the ban and a person decides the claim.", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "profile:read", + "x-required-scope": "sessions:read", "parameters": [ { "schema": { - "type": "string" + "type": "string", + "format": "uuid" }, "required": true, - "name": "session", + "name": "projectId", "in": "path" } ], "responses": { "200": { - "description": "Profile info", + "description": "The project's ban insurance evidence setting", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetProfileResponse" + "$ref": "#/components/schemas/GetProjectInsuranceEvidenceResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -40488,6 +50548,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -40509,6 +50580,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -40530,6 +50612,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -40551,6 +50644,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -40572,38 +50676,45 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API profile:read" - }, - "x-protocols": [ - "Linked Device API" - ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:read`" - } - }, - "/messaging/{session}/profile/name": { + "content": "sessions:read" + } + }, "put": { - "operationId": "setProfileName", + "operationId": "updateProjectInsuranceEvidence", "tags": [ - "Profile" + "Ban insurance" ], - "summary": "Set profile display name (push name)", + "summary": "Set ban insurance evidence setting", + "description": "**Required scope:** `sessions:manage`\n\nBan insurance evidence lets Polymorfa measure what the other devices linked to a number do with it: how many messages they send, to how many people, how fast, and in which hours. Only counts are kept, never messages, contacts, or device names. With it off, a claim can still be filed, but Polymorfa cannot say whether another device caused the ban and a person decides the claim.", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "profile:write", + "x-required-scope": "sessions:manage", "parameters": [ { "schema": { - "type": "string" + "type": "string", + "format": "uuid" }, "required": true, - "name": "session", + "name": "projectId", "in": "path" } ], @@ -40611,20 +50722,40 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SetProfileNameRequest" + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + } + }, + "required": [ + "enabled" + ], + "additionalProperties": false } } } }, "responses": { "200": { - "description": "Name updated", + "description": "The project's ban insurance evidence setting was updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/UpdateProjectInsuranceEvidenceResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -40646,6 +50777,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -40667,6 +50809,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -40688,6 +50841,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -40709,6 +50873,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -40730,59 +50905,102 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "503": { + "description": "Stored, but a connected number did not confirm the change; retry the request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "service_unavailable", + "message": "A required service is unavailable. Check completion before repeating a write.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API profile:write" - }, - "x-protocols": [ - "Linked Device API" - ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" + "content": "sessions:manage" + } } }, - "/messaging/{session}/profile/status": { - "put": { - "operationId": "setProfileStatus", + "/messaging/projects/{projectId}/observation-policy": { + "get": { + "operationId": "getProjectObservationPolicy", "tags": [ - "Profile" + "Presence", + "Labels", + "Business App" ], - "summary": "Set profile status (about) text", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "profile:write", + "x-required-scope": "presence:read", "parameters": [ { "schema": { - "type": "string" + "type": "string", + "format": "uuid" }, "required": true, - "name": "session", + "name": "projectId", "in": "path" } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SetProfileStatusRequest" - } - } - } - }, "responses": { "200": { - "description": "Status updated", + "description": "Project observation ceiling", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/GetProjectObservationPolicyResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -40804,6 +51022,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -40825,6 +51054,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -40846,6 +51086,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -40867,6 +51118,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -40888,59 +51150,71 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API profile:write" + "content": "presence:read" }, - "x-protocols": [ - "Linked Device API" - ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" + "description": "**Required scope:** `presence:read`" } }, - "/messaging/{session}/profile/picture": { - "put": { - "operationId": "setProfilePicture", + "/messaging/{session}/observation-policy": { + "get": { + "operationId": "getSessionObservationPolicy", "tags": [ - "Profile" + "Presence", + "Labels", + "Business App" ], - "summary": "Set profile picture", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "profile:write", + "x-required-scope": "presence:read", "parameters": [ { "schema": { - "type": "string" + "type": "string", + "minLength": 1 }, "required": true, "name": "session", "in": "path" } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/SetProfilePictureRequest" - } - } - } - }, "responses": { "200": { - "description": "Picture updated", + "description": "Effective session observation policy", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/GetSessionObservationPolicyResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -40962,6 +51236,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -40983,6 +51268,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -41004,6 +51300,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -41025,6 +51332,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -41046,29 +51364,42 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API profile:write" + "content": "Linked Device API presence:read" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" - }, - "delete": { - "operationId": "deleteProfilePicture", + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `presence:read`" + } + }, + "/messaging/{session}/profile": { + "get": { + "operationId": "getProfile", "tags": [ "Profile" ], - "summary": "Remove the profile picture", + "summary": "Get the session's own profile", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "profile:write", + "x-required-scope": "profile:read", "parameters": [ { "schema": { @@ -41081,13 +51412,24 @@ ], "responses": { "200": { - "description": "Picture deleted", + "description": "Profile info", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/GetProfileResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -41109,6 +51451,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -41130,6 +51483,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -41151,6 +51515,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -41172,6 +51547,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -41193,31 +51579,42 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API profile:write" + "content": "Linked Device API profile:read" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:read`" } }, - "/messaging/{session}/privacy": { - "get": { - "operationId": "getPrivacySettings", + "/messaging/{session}/profile/name": { + "put": { + "operationId": "setProfileName", "tags": [ - "Privacy" + "Profile" ], - "summary": "Get privacy settings", + "summary": "Set profile display name (push name)", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "profile:read", + "x-required-scope": "profile:write", "parameters": [ { "schema": { @@ -41228,15 +51625,35 @@ "in": "path" } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SetProfileNameRequest" + } + } + } + }, "responses": { "200": { - "description": "Privacy settings", + "description": "Name updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PrivacySettingsResponse" + "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -41258,6 +51675,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -41279,6 +51707,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -41300,6 +51739,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -41321,6 +51771,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -41342,25 +51803,36 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API profile:read" + "content": "Linked Device API profile:write" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:read`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" } }, - "/messaging/{session}/privacy/disappearing/default": { + "/messaging/{session}/profile/status": { "put": { - "operationId": "setDefaultDisappearingTimer", + "operationId": "setProfileStatus", "tags": [ - "Privacy" + "Profile" ], - "summary": "Set the default disappearing-message timer", + "summary": "Set profile status (about) text", "security": [ { "BearerAuth": [] @@ -41381,20 +51853,31 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DefaultDisappearingTimerRequest" + "$ref": "#/components/schemas/SetProfileStatusRequest" } } } }, "responses": { "200": { - "description": "Default disappearing-message timer updated", + "description": "Status updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -41416,6 +51899,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -41437,6 +51931,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -41458,6 +51963,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -41479,6 +51995,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -41500,6 +52027,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -41512,13 +52050,13 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" } }, - "/messaging/{session}/privacy/{setting}": { + "/messaging/{session}/profile/picture": { "put": { - "operationId": "setPrivacySetting", + "operationId": "setProfilePicture", "tags": [ - "Privacy" + "Profile" ], - "summary": "Update a privacy setting", + "summary": "Set profile picture", "security": [ { "BearerAuth": [] @@ -41533,46 +52071,37 @@ "required": true, "name": "session", "in": "path" - }, - { - "schema": { - "type": "string", - "enum": [ - "groupadd", - "last", - "status", - "profile", - "readreceipts", - "online", - "calladd", - "messages", - "defense", - "stickers" - ] - }, - "required": true, - "name": "setting", - "in": "path" } ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SetPrivacySettingRequest" + "$ref": "#/components/schemas/SetProfilePictureRequest" } } } }, "responses": { "200": { - "description": "Updated privacy settings", + "description": "Picture updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdatedPrivacySettingsResponse" + "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -41594,6 +52123,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -41615,6 +52155,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -41636,6 +52187,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -41657,6 +52219,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -41678,6 +52251,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -41688,21 +52272,19 @@ "Linked Device API" ], "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" - } - }, - "/messaging/{session}/labels": { - "get": { - "operationId": "listLabels", + }, + "delete": { + "operationId": "deleteProfilePicture", "tags": [ - "Labels" + "Profile" ], - "summary": "List labels defined for the session", + "summary": "Remove the profile picture", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "labels:read", + "x-required-scope": "profile:write", "parameters": [ { "schema": { @@ -41711,29 +52293,28 @@ "required": true, "name": "session", "in": "path" - }, - { - "schema": { - "type": "string", - "enum": [ - "true", - "false" - ] - }, - "required": false, - "name": "includeObservation", - "in": "query" } ], "responses": { "200": { - "description": "Label list", + "description": "Picture deleted", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ListLabelsResponse" + "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -41755,6 +52336,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -41776,6 +52368,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -41797,6 +52400,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -41818,6 +52432,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -41839,29 +52464,42 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API labels:read" + "content": "Linked Device API profile:write" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `labels:read`" - }, - "post": { - "operationId": "createLabel", + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" + } + }, + "/messaging/{session}/privacy": { + "get": { + "operationId": "getPrivacySettings", "tags": [ - "Labels" + "Privacy" ], - "summary": "Create a label", + "summary": "Get privacy settings", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "labels:manage", + "x-required-scope": "profile:read", "parameters": [ { "schema": { @@ -41872,24 +52510,26 @@ "in": "path" } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateLabelRequest" - } - } - } - }, "responses": { "200": { - "description": "Label created", + "description": "Privacy settings", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateLabelResponse" + "$ref": "#/components/schemas/PrivacySettingsResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -41911,6 +52551,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -41932,6 +52583,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -41953,6 +52615,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -41974,6 +52647,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -41995,31 +52679,42 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API labels:manage" + "content": "Linked Device API profile:read" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `labels:manage`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:read`" } }, - "/messaging/{session}/labels/{labelId}": { + "/messaging/{session}/privacy/disappearing/default": { "put": { - "operationId": "updateLabel", + "operationId": "setDefaultDisappearingTimer", "tags": [ - "Labels" + "Privacy" ], - "summary": "Update a label", + "summary": "Set the default disappearing-message timer", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "labels:manage", + "x-required-scope": "profile:write", "parameters": [ { "schema": { @@ -42028,34 +52723,37 @@ "required": true, "name": "session", "in": "path" - }, - { - "schema": { - "type": "string" - }, - "required": true, - "name": "labelId", - "in": "path" } ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateLabelRequest" + "$ref": "#/components/schemas/DefaultDisappearingTimerRequest" } } } }, "responses": { "200": { - "description": "Label updated", + "description": "Default disappearing-message timer updated", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -42077,6 +52775,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -42098,6 +52807,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -42119,6 +52839,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -42140,26 +52871,16 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } - } - }, - "409": { - "description": "The session has no authoritative view of its labels yet. Retry once a cached or project-retained snapshot is complete.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "conflict_error", - "code": "state_conflict", - "message": "The request conflicts with the resource state.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#state-conflict" - } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -42182,29 +52903,42 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API labels:manage" + "content": "Linked Device API profile:write" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `labels:manage`" - }, - "delete": { - "operationId": "deleteLabel", + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" + } + }, + "/messaging/{session}/privacy/{setting}": { + "put": { + "operationId": "setPrivacySetting", "tags": [ - "Labels" + "Privacy" ], - "summary": "Delete a label", + "summary": "Update a privacy setting", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "labels:manage", + "x-required-scope": "profile:write", "parameters": [ { "schema": { @@ -42216,22 +52950,54 @@ }, { "schema": { - "type": "string" + "type": "string", + "enum": [ + "groupadd", + "last", + "status", + "profile", + "readreceipts", + "online", + "calladd", + "messages", + "defense", + "stickers" + ] }, "required": true, - "name": "labelId", + "name": "setting", "in": "path" } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SetPrivacySettingRequest" + } + } + } + }, "responses": { "200": { - "description": "Label deleted", + "description": "Updated privacy settings", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/UpdatedPrivacySettingsResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -42253,6 +53019,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -42274,6 +53051,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -42295,6 +53083,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -42316,6 +53115,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -42337,25 +53147,36 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API labels:manage" + "content": "Linked Device API profile:write" }, "x-protocols": [ "Linked Device API" ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `labels:manage`" + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `profile:write`" } }, - "/messaging/{session}/labels/chats/{conversation}": { + "/messaging/{session}/labels": { "get": { - "operationId": "getChatLabels", + "operationId": "listLabels", "tags": [ "Labels" ], - "summary": "Get labels attached to a chat", + "summary": "List labels defined for the session", "security": [ { "BearerAuth": [] @@ -42371,14 +53192,6 @@ "name": "session", "in": "path" }, - { - "schema": { - "type": "string" - }, - "required": true, - "name": "conversation", - "in": "path" - }, { "schema": { "type": "string", @@ -42394,13 +53207,24 @@ ], "responses": { "200": { - "description": "Chat labels", + "description": "Label list", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetChatLabelsResponse" + "$ref": "#/components/schemas/ListLabelsResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -42422,6 +53246,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -42443,6 +53278,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -42464,6 +53310,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -42485,6 +53342,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -42506,6 +53374,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -42517,12 +53396,12 @@ ], "description": "**Protocols:** Linked Device API\n\n**Required scope:** `labels:read`" }, - "put": { - "operationId": "setChatLabels", + "post": { + "operationId": "createLabel", "tags": [ "Labels" ], - "summary": "Replace the set of labels attached to a chat", + "summary": "Create a label", "security": [ { "BearerAuth": [] @@ -42537,34 +53416,37 @@ "required": true, "name": "session", "in": "path" - }, - { - "schema": { - "type": "string" - }, - "required": true, - "name": "conversation", - "in": "path" } ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SetChatLabelsRequest" + "$ref": "#/components/schemas/CreateLabelRequest" } } } }, "responses": { "200": { - "description": "Chat labels updated", + "description": "Label created", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/CreateLabelResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -42586,6 +53468,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -42607,6 +53500,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -42628,6 +53532,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -42649,26 +53564,16 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } - } - }, - "409": { - "description": "The session has no authoritative view of its labels yet. Retry once a cached or project-retained snapshot is complete.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "conflict_error", - "code": "state_conflict", - "message": "The request conflicts with the resource state.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#state-conflict" - } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -42691,6 +53596,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -42703,28 +53619,66 @@ "description": "**Protocols:** Linked Device API\n\n**Required scope:** `labels:manage`" } }, - "/messaging/webhooks": { - "get": { - "operationId": "listWebhooks", + "/messaging/{session}/labels/{labelId}": { + "put": { + "operationId": "updateLabel", "tags": [ - "Webhooks" + "Labels" ], - "summary": "List webhooks for the tenant", + "summary": "Update a label", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "webhooks:manage", + "x-required-scope": "labels:manage", + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "session", + "in": "path" + }, + { + "schema": { + "type": "string" + }, + "required": true, + "name": "labelId", + "in": "path" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateLabelRequest" + } + } + } + }, "responses": { "200": { - "description": "Webhook list", + "description": "Label updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ListWebhooksResponse" + "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -42746,6 +53700,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -42767,6 +53732,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -42788,6 +53764,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -42809,6 +53796,49 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "409": { + "description": "The session has no authoritative view of its labels yet. Retry once a cached or project-retained snapshot is complete.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "conflict_error", + "code": "state_conflict", + "message": "The request conflicts with the resource state.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#state-conflict" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -42830,44 +53860,78 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "webhooks:manage" + "content": "Linked Device API labels:manage" }, - "description": "**Required scope:** `webhooks:manage`" + "x-protocols": [ + "Linked Device API" + ], + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `labels:manage`" }, - "post": { - "operationId": "createWebhook", + "delete": { + "operationId": "deleteLabel", "tags": [ - "Webhooks" + "Labels" ], - "summary": "Register a new webhook", + "summary": "Delete a label", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "webhooks:manage", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateWebhookRequest" - } - } + "x-required-scope": "labels:manage", + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "session", + "in": "path" + }, + { + "schema": { + "type": "string" + }, + "required": true, + "name": "labelId", + "in": "path" } - }, + ], "responses": { - "201": { - "description": "Webhook created", + "200": { + "description": "Label deleted", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateWebhookResponse" + "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -42889,6 +53953,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -42910,6 +53985,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -42931,6 +54017,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -42952,6 +54049,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -42973,47 +54081,92 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "webhooks:manage" + "content": "Linked Device API labels:manage" }, - "description": "**Required scope:** `webhooks:manage`" + "x-protocols": [ + "Linked Device API" + ], + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `labels:manage`" } }, - "/messaging/webhooks/{id}": { + "/messaging/{session}/labels/chats/{conversation}": { "get": { - "operationId": "getWebhook", + "operationId": "getChatLabels", "tags": [ - "Webhooks" + "Labels" ], - "summary": "Get a webhook by id", + "summary": "Get labels attached to a chat", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "webhooks:manage", + "x-required-scope": "labels:read", "parameters": [ { "schema": { "type": "string" }, "required": true, - "name": "id", + "name": "session", + "in": "path" + }, + { + "schema": { + "type": "string" + }, + "required": true, + "name": "conversation", "in": "path" + }, + { + "schema": { + "type": "string", + "enum": [ + "true", + "false" + ] + }, + "required": false, + "name": "includeObservation", + "in": "query" } ], "responses": { "200": { - "description": "Webhook details", + "description": "Chat labels", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetWebhookResponse" + "$ref": "#/components/schemas/GetChatLabelsResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -43035,6 +54188,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -43056,6 +54220,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -43077,6 +54252,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -43098,6 +54284,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -43119,33 +54316,55 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "webhooks:manage" + "content": "Linked Device API labels:read" }, - "description": "**Required scope:** `webhooks:manage`" + "x-protocols": [ + "Linked Device API" + ], + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `labels:read`" }, "put": { - "operationId": "updateWebhook", + "operationId": "setChatLabels", "tags": [ - "Webhooks" + "Labels" ], - "summary": "Update a webhook", + "summary": "Replace the set of labels attached to a chat", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "webhooks:manage", + "x-required-scope": "labels:manage", "parameters": [ { "schema": { "type": "string" }, "required": true, - "name": "id", + "name": "session", + "in": "path" + }, + { + "schema": { + "type": "string" + }, + "required": true, + "name": "conversation", "in": "path" } ], @@ -43153,20 +54372,31 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateWebhookRequest" + "$ref": "#/components/schemas/SetChatLabelsRequest" } } } }, "responses": { "200": { - "description": "Webhook updated", + "description": "Chat labels updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateWebhookResponse" + "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -43188,6 +54418,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -43209,6 +54450,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -43230,6 +54482,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -43251,6 +54514,49 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "409": { + "description": "The session has no authoritative view of its labels yet. Retry once a cached or project-retained snapshot is complete.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "conflict_error", + "code": "state_conflict", + "message": "The request conflicts with the resource state.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#state-conflict" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -43272,45 +54578,62 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "webhooks:manage" + "content": "Linked Device API labels:manage" }, - "description": "**Required scope:** `webhooks:manage`" - }, - "delete": { - "operationId": "deleteWebhook", + "x-protocols": [ + "Linked Device API" + ], + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `labels:manage`" + } + }, + "/messaging/webhooks": { + "get": { + "operationId": "listWebhooks", "tags": [ "Webhooks" ], - "summary": "Delete a webhook", + "summary": "List webhooks for the tenant", "security": [ { "BearerAuth": [] } ], "x-required-scope": "webhooks:manage", - "parameters": [ - { - "schema": { - "type": "string" - }, - "required": true, - "name": "id", - "in": "path" - } - ], "responses": { "200": { - "description": "Webhook deleted", + "description": "Webhook list", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/ListWebhooksResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -43332,6 +54655,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -43353,6 +54687,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -43374,6 +54719,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -43395,6 +54751,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -43416,6 +54783,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -43423,37 +54801,49 @@ "content": "webhooks:manage" }, "description": "**Required scope:** `webhooks:manage`" - } - }, - "/messaging/media/{id}": { - "get": { - "operationId": "downloadMedia", + }, + "post": { + "operationId": "createWebhook", "tags": [ - "Media" + "Webhooks" ], - "summary": "Download media (binary stream or redirect to S3)", + "summary": "Register a new webhook", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "media:read", - "parameters": [ - { - "schema": { - "type": "string" - }, - "required": true, - "name": "id", - "in": "path" + "x-required-scope": "webhooks:manage", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateWebhookRequest" + } + } } - ], + }, "responses": { - "200": { - "description": "Media binary stream" - }, - "302": { - "description": "Redirect to S3" + "201": { + "description": "Webhook created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateWebhookResponse" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } }, "400": { "description": "Bad request — request body or params failed validation", @@ -43474,6 +54864,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -43495,6 +54896,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -43516,6 +54928,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -43537,6 +54960,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -43558,31 +54992,39 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API media:read" + "content": "webhooks:manage" }, - "x-protocols": [ - "Linked Device API" - ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `media:read`" + "description": "**Required scope:** `webhooks:manage`" } }, - "/messaging/media/{id}/info": { + "/messaging/webhooks/{id}": { "get": { - "operationId": "getMediaInfo", + "operationId": "getWebhook", "tags": [ - "Media" + "Webhooks" ], - "summary": "Get media metadata", + "summary": "Get a webhook by id", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "media:read", + "x-required-scope": "webhooks:manage", "parameters": [ { "schema": { @@ -43595,13 +55037,24 @@ ], "responses": { "200": { - "description": "Media info", + "description": "Webhook details", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetMediaInfoResponse" + "$ref": "#/components/schemas/GetWebhookResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -43623,6 +55076,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -43644,6 +55108,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -43665,6 +55140,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -43686,6 +55172,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -43707,31 +55204,37 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API media:read" + "content": "webhooks:manage" }, - "x-protocols": [ - "Linked Device API" - ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `media:read`" - } - }, - "/messaging/media/{id}/download-and-save": { - "post": { - "operationId": "downloadAndSaveMedia", + "description": "**Required scope:** `webhooks:manage`" + }, + "put": { + "operationId": "updateWebhook", "tags": [ - "Media" + "Webhooks" ], - "summary": "Download media and persist it to the tenant's S3 bucket", + "summary": "Update a webhook", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "media:manage", + "x-required-scope": "webhooks:manage", "parameters": [ { "schema": { @@ -43742,15 +55245,35 @@ "in": "path" } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdateWebhookRequest" + } + } + } + }, "responses": { "200": { - "description": "Persistence queued or already persisted", + "description": "Webhook updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/UpdateWebhookResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -43772,6 +55295,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -43793,6 +55327,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -43814,6 +55359,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -43835,6 +55391,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -43856,50 +55423,67 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "Linked Device API media:manage" + "content": "webhooks:manage" }, - "x-protocols": [ - "Linked Device API" - ], - "description": "**Protocols:** Linked Device API\n\n**Required scope:** `media:manage`" - } - }, - "/messaging/projects/{projectSlug}/templates": { - "get": { - "operationId": "listProjectTemplates", + "description": "**Required scope:** `webhooks:manage`" + }, + "delete": { + "operationId": "deleteWebhook", "tags": [ - "Templates" + "Webhooks" ], - "summary": "List project message templates", + "summary": "Delete a webhook", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "templates:read", + "x-required-scope": "webhooks:manage", "parameters": [ { "schema": { "type": "string" }, "required": true, - "name": "projectSlug", + "name": "id", "in": "path" } ], "responses": { "200": { - "description": "Templates", + "description": "Webhook deleted", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ListProjectTemplatesResponse" + "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -43921,6 +55505,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -43942,6 +55537,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -43963,6 +55569,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -43984,6 +55601,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -44005,53 +55633,75 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "templates:read" + "content": "webhooks:manage" }, - "description": "**Required scope:** `templates:read`" - }, - "post": { - "operationId": "createProjectTemplate", + "description": "**Required scope:** `webhooks:manage`" + } + }, + "/messaging/media/{id}": { + "get": { + "operationId": "downloadMedia", "tags": [ - "Templates" + "Media" ], - "summary": "Create a project message template", + "summary": "Download media (binary stream or redirect to S3)", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "templates:manage", + "x-required-scope": "media:read", "parameters": [ { "schema": { "type": "string" }, "required": true, - "name": "projectSlug", + "name": "id", "in": "path" } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateMessageTemplateRequest" + "responses": { + "200": { + "description": "Media binary stream", + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } - } - }, - "responses": { - "201": { - "description": "Created", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateProjectTemplateResponse" - } + }, + "302": { + "description": "Redirect to S3", + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -44074,6 +55724,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -44095,6 +55756,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -44116,6 +55788,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -44137,6 +55820,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -44158,37 +55852,43 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "templates:manage" + "content": "Linked Device API media:read" }, - "description": "**Required scope:** `templates:manage`" + "x-protocols": [ + "Linked Device API" + ], + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `media:read`" } }, - "/messaging/projects/{projectSlug}/templates/{id}": { + "/messaging/media/{id}/info": { "get": { - "operationId": "getProjectTemplate", + "operationId": "getMediaInfo", "tags": [ - "Templates" + "Media" ], - "summary": "Get a project message template", + "summary": "Get media metadata", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "templates:read", + "x-required-scope": "media:read", "parameters": [ - { - "schema": { - "type": "string" - }, - "required": true, - "name": "projectSlug", - "in": "path" - }, { "schema": { "type": "string" @@ -44200,13 +55900,24 @@ ], "responses": { "200": { - "description": "Template", + "description": "Media info", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetProjectTemplateResponse" + "$ref": "#/components/schemas/GetMediaInfoResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -44228,6 +55939,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -44249,6 +55971,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -44270,6 +56003,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -44291,6 +56035,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -44312,35 +56067,43 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "templates:read" + "content": "Linked Device API media:read" }, - "description": "**Required scope:** `templates:read`" - }, - "patch": { - "operationId": "updateProjectTemplate", + "x-protocols": [ + "Linked Device API" + ], + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `media:read`" + } + }, + "/messaging/media/{id}/download-and-save": { + "post": { + "operationId": "downloadAndSaveMedia", "tags": [ - "Templates" + "Media" ], - "summary": "Update a project message template", + "summary": "Download media and persist it to the tenant's S3 bucket", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "templates:manage", + "x-required-scope": "media:manage", "parameters": [ - { - "schema": { - "type": "string" - }, - "required": true, - "name": "projectSlug", - "in": "path" - }, { "schema": { "type": "string" @@ -44350,24 +56113,26 @@ "in": "path" } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/UpdateMessageTemplateRequest" - } - } - } - }, "responses": { "200": { - "description": "Updated", + "description": "Persistence queued or already persisted", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateProjectTemplateResponse" + "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -44389,6 +56154,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -44410,6 +56186,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -44431,6 +56218,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -44452,6 +56250,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -44473,51 +56282,60 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "templates:manage" + "content": "Linked Device API media:manage" }, - "description": "**Required scope:** `templates:manage`" - }, - "delete": { - "operationId": "deleteProjectTemplate", + "x-protocols": [ + "Linked Device API" + ], + "description": "**Protocols:** Linked Device API\n\n**Required scope:** `media:manage`" + } + }, + "/messaging/{session}/templates": { + "get": { + "operationId": "listTemplates", "tags": [ "Templates" ], - "summary": "Delete a project message template", + "summary": "List templates for a Number", + "description": "**Protocols:** Meta Cloud API\n\n**Required scope:** `messages:write`\n\nFor a real Official API Number, reads the complete template catalog of its WhatsApp Business Account from Meta, stores it, and returns it. A test-mode Cloud Number uses its simulated catalog. A Linked Devices Number, including one that later added an official connection, receives 400. If a catalog read is incomplete, the request fails with 502 and the stored templates are unchanged.", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "templates:manage", + "x-required-scope": "messages:write", "parameters": [ { "schema": { "type": "string" }, "required": true, - "name": "projectSlug", - "in": "path" - }, - { - "schema": { - "type": "string" - }, - "required": true, - "name": "id", + "name": "session", "in": "path" } ], "responses": { "200": { - "description": "Deleted", + "description": "Template list", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SuccessResponse" + "$ref": "#/components/schemas/ListTemplatesResponse" } } } @@ -44626,130 +56444,9 @@ } } } - } - }, - "x-mint": { - "content": "templates:manage" - }, - "description": "**Required scope:** `templates:manage`" - } - }, - "/messaging/projects/{projectSlug}/templates/{id}/preview": { - "post": { - "operationId": "previewProjectTemplate", - "tags": [ - "Templates" - ], - "summary": "Render a template preview (idealized) or surface-specific output", - "security": [ - { - "BearerAuth": [] - } - ], - "x-required-scope": "templates:read", - "parameters": [ - { - "schema": { - "type": "string" - }, - "required": true, - "name": "projectSlug", - "in": "path" - }, - { - "schema": { - "type": "string" - }, - "required": true, - "name": "id", - "in": "path" - } - ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PreviewMessageTemplateRequest" - } - } - } - }, - "responses": { - "200": { - "description": "Rendered", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PreviewProjectTemplateResponse" - } - } - } - }, - "400": { - "description": "Bad request — request body or params failed validation", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "invalid_request_error", - "code": "invalid_parameter", - "message": "Correct the invalid request field.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" - } - } - } - }, - "401": { - "description": "Missing or invalid authentication token", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "authentication_error", - "code": "invalid_credential", - "message": "Supply a valid credential.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" - } - } - } }, - "403": { - "description": "Authenticated but not permitted (missing scope or rule violation)", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "permission_error", - "code": "permission_denied", - "message": "The credential does not have access to this resource.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#permission-denied" - } - } - } - }, - "404": { - "description": "Resource not found", + "502": { + "description": "Meta did not return a complete template catalog; the stored templates are unchanged", "content": { "application/json": { "schema": { @@ -44757,20 +56454,20 @@ }, "example": { "error": { - "type": "invalid_request_error", - "code": "resource_not_found", - "message": "The resource is unavailable to this caller.", + "type": "upstream_error", + "code": "upstream_failure", + "message": "The messaging provider could not complete the request.", "param": null, "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" }, "data": null, - "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" + "docs": "https://docs.polymorfa.com/api/errors#upstream-failure" } } } }, - "500": { - "description": "Internal server error", + "503": { + "description": "The Number's WhatsApp Business Account is not available", "content": { "application/json": { "schema": { @@ -44779,72 +56476,75 @@ "example": { "error": { "type": "api_error", - "code": "internal_error", - "message": "An unexpected service failure occurred. Keep the request ID for support.", + "code": "service_unavailable", + "message": "A required service is unavailable. Check completion before repeating a write.", "param": null, "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" }, "data": null, - "docs": "https://docs.polymorfa.com/api/errors#internal-error" + "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" } } } } }, "x-mint": { - "content": "templates:read" + "content": "Meta Cloud API messages:write" }, - "description": "**Required scope:** `templates:read`" - } - }, - "/messaging/projects/{projectSlug}/templates/{id}/submit": { + "x-protocols": [ + "Meta Cloud API" + ] + }, "post": { - "operationId": "submitProjectTemplateToMeta", + "operationId": "createTemplate", "tags": [ "Templates" ], - "summary": "Submit a project template to Meta for approval on a Cloud-API session", - "description": "**Required scope:** `templates:manage`\n\nRenders the canonical template into a Meta create-template payload and submits it through the given cloud_api session. Records a per-session approval pointer (cloudLinks) and returns the updated template. The Meta review verdict arrives later via webhook.", + "summary": "Create a template for a Number", + "description": "**Protocols:** Meta Cloud API\n\n**Required scope:** `messages:write`\n\nSubmits the template to Meta for a real Official API Number or to the simulator for a test-mode Cloud Number, then stores the receipt. If the provider accepts the template but the receipt cannot be stored, the API imports it from one catalog read; if that also fails, it returns 500 with `template was submitted but could not be stored`. Do not resubmit in that case: list templates to load it. A retry with the same `Idempotency-Key` returns `409 idempotency_completed`. The returned status is the one in the response, or `PENDING` when none is returned. For real Official API Numbers, later approval, rejection and quality changes arrive as `template.status` webhook events and appear in the next list. A Linked Devices Number, including one that later added an official connection, receives 400.", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "templates:manage", + "x-required-scope": "messages:write", "parameters": [ { "schema": { "type": "string" }, "required": true, - "name": "projectSlug", + "name": "session", "in": "path" }, { + "name": "Idempotency-Key", + "in": "header", + "required": false, + "description": "Unique key, 1 to 255 bytes of UTF-8, that makes this write safe to retry for 24 hours. A retry with the same key, credential, `Polymorfa-Version`, and request never runs the write again: it returns `409 idempotency_completed` when the original succeeded, or the original server error, with `Idempotent-Replayed: true`. Reusing the key for a different request returns `409 idempotency_conflict`; a retry while the original is still running returns `409 idempotency_in_progress`, and a retry after an unrecorded outcome returns `409 idempotency_outcome_unknown`.", "schema": { - "type": "string" - }, - "required": true, - "name": "id", - "in": "path" + "type": "string", + "minLength": 1, + "maxLength": 255 + } } ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SubmitMessageTemplateRequest" + "$ref": "#/components/schemas/CreateTemplateRequest" } } } }, "responses": { - "200": { - "description": "Submitted", + "201": { + "description": "Template submitted", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/SubmitProjectTemplateResponse" + "$ref": "#/components/schemas/CreateTemplateResponse" } } } @@ -44933,6 +56633,44 @@ } } }, + "409": { + "description": "The Idempotency-Key already succeeded (`idempotency_completed`), was used for a different request (`idempotency_conflict`), belongs to a request that is still running (`idempotency_in_progress`, with Retry-After), or belongs to a request whose outcome was not recorded (`idempotency_outcome_unknown`)", + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying `idempotency_in_progress`.", + "schema": { + "type": "integer", + "minimum": 0 + } + }, + "Idempotent-Replayed": { + "description": "`true` when this response reports the recorded outcome of an earlier request with the same Idempotency-Key.", + "schema": { + "type": "string", + "enum": [ + "true" + ] + } + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "conflict_error", + "code": "idempotency_conflict", + "message": "This Idempotency-Key was already used with a different request. Use a new key for a different request.", + "param": null + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#idempotency-conflict" + } + } + } + }, "500": { "description": "Internal server error", "content": { @@ -44953,43 +56691,126 @@ } } } + }, + "502": { + "description": "Meta did not complete the request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "upstream_error", + "code": "upstream_failure", + "message": "The messaging provider could not complete the request.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#upstream-failure" + } + } + } + }, + "503": { + "description": "The Number's WhatsApp Business Account is not available", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "service_unavailable", + "message": "A required service is unavailable. Check completion before repeating a write.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" + } + } + } + }, + "default": { + "description": "Meta returned a 400-599 error status; the mutation may have an unknown outcome", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "internal_error", + "message": "An unexpected service failure occurred. Keep the request ID for support.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#internal-error" + } + } + } } }, "x-mint": { - "content": "templates:manage" - } + "content": "Meta Cloud API messages:write" + }, + "x-protocols": [ + "Meta Cloud API" + ] } }, - "/messaging/projects/{projectSlug}/campaigns": { + "/messaging/{session}/templates/{name}": { "get": { - "operationId": "listCampaigns", + "operationId": "getTemplate", "tags": [ - "Campaigns" + "Templates" ], - "summary": "List campaigns", + "summary": "Get a template by name", + "description": "**Protocols:** Meta Cloud API\n\n**Required scope:** `messages:write`\n\nRefreshes the Number's provider or simulated template catalog and returns the template with this name and language. `language` defaults to `en_US`. Fails with 502 if the catalog read is incomplete. Real Official API and test-mode Cloud Numbers are supported; a Linked Devices Number, including one that later added an official connection, receives 400.", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "campaigns:read", + "x-required-scope": "messages:write", "parameters": [ { "schema": { "type": "string" }, "required": true, - "name": "projectSlug", + "name": "session", "in": "path" + }, + { + "schema": { + "type": "string" + }, + "required": true, + "name": "name", + "in": "path" + }, + { + "schema": { + "type": "string" + }, + "required": false, + "name": "language", + "in": "query" } ], "responses": { "200": { - "description": "OK", + "description": "Template detail", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ListCampaignsResponse" + "$ref": "#/components/schemas/GetTemplateResponse" } } } @@ -45098,33 +56919,85 @@ } } } + }, + "502": { + "description": "Meta did not return a complete template catalog; the stored templates are unchanged", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "upstream_error", + "code": "upstream_failure", + "message": "The messaging provider could not complete the request.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#upstream-failure" + } + } + } + }, + "503": { + "description": "The Number's WhatsApp Business Account is not available", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "service_unavailable", + "message": "A required service is unavailable. Check completion before repeating a write.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" + } + } + } } }, "x-mint": { - "content": "campaigns:read" + "content": "Meta Cloud API messages:write" }, - "description": "**Required scope:** `campaigns:read`" + "x-protocols": [ + "Meta Cloud API" + ] }, - "post": { - "operationId": "createCampaign", + "delete": { + "operationId": "deleteTemplate", "tags": [ - "Campaigns" + "Templates" ], - "summary": "Create a campaign", - "description": "**Required scope:** `campaigns:manage`\n\nCreates a draft campaign on any plan. `senderConfig.sessionIds` may name numbers that are not yet on a plan that includes Campaigns; launch checks the selection. It is refused with `403 bansafe_org_suspended` while the team is suspended and `400` when `senderConfig.sessionIds` is malformed or a per-number cap is above its safety ceiling.", + "summary": "Delete a template", + "description": "**Protocols:** Meta Cloud API\n\n**Required scope:** `messages:write`\n\nDeletes every language of the named template from Meta for a real Official API Number or from the simulated catalog for a test-mode Cloud Number, then removes the stored copies. A confirmed provider deletion succeeds even if local cleanup fails; the next complete catalog read reconciles the mirror. If the provider rejects the deletion or its result is unknown, the stored templates are kept. A Linked Devices Number, including one that later added an official connection, receives 400.", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "campaigns:manage", + "x-required-scope": "messages:write", "parameters": [ { "schema": { "type": "string" }, "required": true, - "name": "projectSlug", + "name": "session", + "in": "path" + }, + { + "schema": { + "type": "string" + }, + "required": true, + "name": "name", "in": "path" }, { @@ -45139,22 +57012,13 @@ } } ], - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateCampaignRequest" - } - } - } - }, "responses": { "200": { - "description": "OK", + "description": "Template deleted", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CreateCampaignResponse" + "$ref": "#/components/schemas/SuccessResponse" } } } @@ -45301,26 +57165,71 @@ } } } + }, + "502": { + "description": "Meta did not complete the request", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "upstream_error", + "code": "upstream_failure", + "message": "The messaging provider could not complete the request.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#upstream-failure" + } + } + } + }, + "default": { + "description": "Meta returned a 400-599 error status; the mutation may have an unknown outcome", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "internal_error", + "message": "An unexpected service failure occurred. Keep the request ID for support.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#internal-error" + } + } + } } }, "x-mint": { - "content": "campaigns:manage" - } + "content": "Meta Cloud API messages:write" + }, + "x-protocols": [ + "Meta Cloud API" + ] } }, - "/messaging/projects/{projectSlug}/campaigns/{id}": { + "/messaging/projects/{projectSlug}/templates": { "get": { - "operationId": "getCampaign", + "operationId": "listProjectTemplates", "tags": [ - "Campaigns" + "Templates" ], - "summary": "Get a campaign", + "summary": "List project message templates", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "campaigns:read", + "x-required-scope": "templates:read", "parameters": [ { "schema": { @@ -45329,25 +57238,28 @@ "required": true, "name": "projectSlug", "in": "path" - }, - { - "schema": { - "type": "string" - }, - "required": true, - "name": "id", - "in": "path" } ], "responses": { "200": { - "description": "OK", + "description": "Templates", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/GetCampaignResponse" + "$ref": "#/components/schemas/ListProjectTemplatesResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -45369,6 +57281,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -45390,6 +57313,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -45411,6 +57345,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -45432,6 +57377,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -45453,27 +57409,37 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "campaigns:read" + "content": "templates:read" }, - "description": "**Required scope:** `campaigns:read`" + "description": "**Required scope:** `templates:read`" }, - "patch": { - "operationId": "updateProjectCampaign", + "post": { + "operationId": "createProjectTemplate", "tags": [ - "Campaigns" + "Templates" ], - "summary": "Update a campaign", - "description": "**Required scope:** `campaigns:manage`\n\nUpdates the fields you send and leaves the rest unchanged. On an unlaunched draft, a new `senderConfig` passes the same checks as create, on any plan; launch checks the selection against live plans. Once the campaign has launched, including a scheduled launch that has not started, a new `senderConfig` must pass the launch checks and can answer `402 campaigns_not_entitled` or `400 invalid_parameter`. `recipientListId` and a different `scheduledAt` are accepted only while the campaign is an unlaunched draft; afterwards they answer `409`. A sender change to a draft that launches while the change is being saved also answers `409`. A non-null `scheduledAt` must be integer Unix milliseconds within the JavaScript Date range.", + "summary": "Create a project message template", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "campaigns:manage", + "x-required-scope": "templates:manage", "parameters": [ { "schema": { @@ -45482,36 +57448,37 @@ "required": true, "name": "projectSlug", "in": "path" - }, - { - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true, - "name": "id", - "in": "path" } ], "requestBody": { - "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateCampaignRequest" + "$ref": "#/components/schemas/CreateMessageTemplateRequest" } } } }, "responses": { - "200": { - "description": "OK", + "201": { + "description": "Created", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/UpdateCampaignResponse" + "$ref": "#/components/schemas/CreateProjectTemplateResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -45533,6 +57500,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -45554,26 +57532,16 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } - } - }, - "402": { - "description": "`campaigns_not_entitled`: a new sender selection on a launched campaign names a number whose plan does not include Campaigns", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "permission_error", - "code": "campaigns_not_entitled", - "message": "This number’s plan does not include Campaigns.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#campaigns-not-entitled" - } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -45596,6 +57564,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -45617,26 +57596,16 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } - } - }, - "409": { - "description": "`state_conflict`: the campaign has launched and the audience or start time can no longer change, or it launched while its sending numbers were being changed", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "conflict_error", - "code": "state_conflict", - "message": "The request conflicts with the resource state.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#state-conflict" - } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -45659,48 +57628,39 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } - } - }, - "503": { - "description": "`service_unavailable`: number eligibility for a launched campaign's new sender selection could not be checked. Nothing was changed", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "api_error", - "code": "service_unavailable", - "message": "A required service is unavailable. Check completion before repeating a write.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" - } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } } }, "x-mint": { - "content": "campaigns:manage" - } + "content": "templates:manage" + }, + "description": "**Required scope:** `templates:manage`" } }, - "/messaging/projects/{projectSlug}/campaigns/{id}/analytics": { + "/messaging/projects/{projectSlug}/templates/{id}": { "get": { - "operationId": "getCampaignAnalytics", + "operationId": "getProjectTemplate", "tags": [ - "Campaigns" + "Templates" ], - "summary": "Campaign analytics (funnel, response timing, retries)", + "summary": "Get a project message template", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "campaigns:read", + "x-required-scope": "templates:read", "parameters": [ { "schema": { @@ -45721,13 +57681,24 @@ ], "responses": { "200": { - "description": "OK", + "description": "Template", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/CampaignAnalyticsResponse" + "$ref": "#/components/schemas/GetProjectTemplateResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -45749,6 +57720,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -45770,6 +57752,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -45791,6 +57784,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -45812,6 +57816,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -45833,29 +57848,37 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "campaigns:read" + "content": "templates:read" }, - "description": "**Required scope:** `campaigns:read`" - } - }, - "/messaging/projects/{projectSlug}/campaigns/{id}/launch": { - "post": { - "operationId": "launchCampaign", + "description": "**Required scope:** `templates:read`" + }, + "patch": { + "operationId": "updateProjectTemplate", "tags": [ - "Campaigns" + "Templates" ], - "summary": "Launch a campaign", - "description": "**Required scope:** `campaigns:manage`\n\nStarts a draft campaign. It begins sending at `scheduledAt`, or right away when no start time is set. Launch checks the saved sender selection against live plans: `402 campaigns_not_entitled` when a selected number is missing or its plan does not include Campaigns, or when an unselected campaign's project has no such number; `400 invalid_parameter` on `senderConfig.sessionIds` when a selected number is not connected or the selection mixes sending types; `400 campaign_throughput_capped` when a requested throughput is above its ceiling. The response returns the campaign as it was when the launch was accepted.", + "summary": "Update a project message template", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "campaigns:manage", + "x-required-scope": "templates:manage", "parameters": [ { "schema": { @@ -45872,37 +57895,37 @@ "required": true, "name": "id", "in": "path" - }, - { - "name": "Idempotency-Key", - "in": "header", - "required": false, - "description": "Unique key, 1 to 255 bytes of UTF-8, that makes this write safe to retry for 24 hours. A retry with the same key, credential, `Polymorfa-Version`, and request never runs the write again: it returns `409 idempotency_completed` when the original succeeded, or the original server error, with `Idempotent-Replayed: true`. Reusing the key for a different request returns `409 idempotency_conflict`; a retry while the original is still running returns `409 idempotency_in_progress`, and a retry after an unrecorded outcome returns `409 idempotency_outcome_unknown`.", - "schema": { - "type": "string", - "minLength": 1, - "maxLength": 255 - } } ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/LaunchCampaignRequest" + "$ref": "#/components/schemas/UpdateMessageTemplateRequest" } } } }, "responses": { "200": { - "description": "OK", + "description": "Updated", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/LaunchCampaignResponse" + "$ref": "#/components/schemas/UpdateProjectTemplateResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -45924,10 +57947,21 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } - } - }, - "401": { - "description": "Missing or invalid authentication token", + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "401": { + "description": "Missing or invalid authentication token", "content": { "application/json": { "schema": { @@ -45945,26 +57979,16 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } - } - }, - "402": { - "description": "`campaigns_not_entitled`: a selected number is missing or its plan does not include Campaigns, or the project has no number on such a plan. Drafts are saved on every plan.", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "permission_error", - "code": "campaigns_not_entitled", - "message": "This number’s plan does not include Campaigns.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#campaigns-not-entitled" - } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -45987,6 +58011,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -46008,44 +58043,16 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } - } - }, - "409": { - "description": "`state_conflict`: The campaign is not a draft, it is already launched and waiting for its start time, or its sending numbers changed during the launch. The Idempotency-Key already succeeded (`idempotency_completed`), was used for a different request (`idempotency_conflict`), belongs to a request that is still running (`idempotency_in_progress`, with Retry-After), or belongs to a request whose outcome was not recorded (`idempotency_outcome_unknown`)", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "conflict_error", - "code": "state_conflict", - "message": "The request conflicts with the resource state.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#state-conflict" - } - } }, "headers": { - "Retry-After": { - "description": "Seconds to wait before retrying `idempotency_in_progress`.", - "schema": { - "type": "integer", - "minimum": 0 - } + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" }, - "Idempotent-Replayed": { - "description": "`true` when this response reports the recorded outcome of an earlier request with the same Idempotency-Key.", - "schema": { - "type": "string", - "enum": [ - "true" - ] - } + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -46068,49 +58075,37 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } - } - }, - "503": { - "description": "`service_unavailable`: the selected numbers' plans could not be checked; retry", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "api_error", - "code": "service_unavailable", - "message": "A required service is unavailable. Check completion before repeating a write.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" - } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } } }, "x-mint": { - "content": "campaigns:manage" - } - } - }, - "/messaging/projects/{projectSlug}/campaigns/{id}/pause": { - "post": { - "operationId": "pauseCampaign", + "content": "templates:manage" + }, + "description": "**Required scope:** `templates:manage`" + }, + "delete": { + "operationId": "deleteProjectTemplate", "tags": [ - "Campaigns" + "Templates" ], - "summary": "Pause a campaign", - "description": "**Required scope:** `campaigns:manage`\n\nPauses a running campaign. Sends already in progress can still finish.", + "summary": "Delete a project message template", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "campaigns:manage", + "x-required-scope": "templates:manage", "parameters": [ { "schema": { @@ -46127,28 +58122,28 @@ "required": true, "name": "id", "in": "path" - }, - { - "name": "Idempotency-Key", - "in": "header", - "required": false, - "description": "Unique key, 1 to 255 bytes of UTF-8, that makes this write safe to retry for 24 hours. A retry with the same key, credential, `Polymorfa-Version`, and request never runs the write again: it returns `409 idempotency_completed` when the original succeeded, or the original server error, with `Idempotent-Replayed: true`. Reusing the key for a different request returns `409 idempotency_conflict`; a retry while the original is still running returns `409 idempotency_in_progress`, and a retry after an unrecorded outcome returns `409 idempotency_outcome_unknown`.", - "schema": { - "type": "string", - "minLength": 1, - "maxLength": 255 - } } ], "responses": { "200": { - "description": "OK", + "description": "Deleted", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PauseCampaignResponse" + "$ref": "#/components/schemas/SuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -46170,6 +58165,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -46191,6 +58197,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -46212,6 +58229,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -46233,44 +58261,16 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } - } - }, - "409": { - "description": "`state_conflict`: The campaign is not running, or it has no active delivery run to pause. The Idempotency-Key already succeeded (`idempotency_completed`), was used for a different request (`idempotency_conflict`), belongs to a request that is still running (`idempotency_in_progress`, with Retry-After), or belongs to a request whose outcome was not recorded (`idempotency_outcome_unknown`)", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "conflict_error", - "code": "state_conflict", - "message": "The request conflicts with the resource state.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#state-conflict" - } - } }, "headers": { - "Retry-After": { - "description": "Seconds to wait before retrying `idempotency_in_progress`.", - "schema": { - "type": "integer", - "minimum": 0 - } + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" }, - "Idempotent-Replayed": { - "description": "`true` when this response reports the recorded outcome of an earlier request with the same Idempotency-Key.", - "schema": { - "type": "string", - "enum": [ - "true" - ] - } + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -46293,28 +58293,39 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "campaigns:manage" - } + "content": "templates:manage" + }, + "description": "**Required scope:** `templates:manage`" } }, - "/messaging/projects/{projectSlug}/campaigns/{id}/resume": { + "/messaging/projects/{projectSlug}/templates/{id}/preview": { "post": { - "operationId": "resumeCampaign", + "operationId": "previewProjectTemplate", "tags": [ - "Campaigns" + "Templates" ], - "summary": "Resume a campaign", - "description": "**Required scope:** `campaigns:manage`\n\nResumes a paused campaign. Its remaining recipients are sent after the same checks as before the pause.", + "summary": "Render a template preview (idealized) or surface-specific output", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "campaigns:manage", + "x-required-scope": "templates:read", "parameters": [ { "schema": { @@ -46331,28 +58342,37 @@ "required": true, "name": "id", "in": "path" - }, - { - "name": "Idempotency-Key", - "in": "header", - "required": false, - "description": "Unique key, 1 to 255 bytes of UTF-8, that makes this write safe to retry for 24 hours. A retry with the same key, credential, `Polymorfa-Version`, and request never runs the write again: it returns `409 idempotency_completed` when the original succeeded, or the original server error, with `Idempotent-Replayed: true`. Reusing the key for a different request returns `409 idempotency_conflict`; a retry while the original is still running returns `409 idempotency_in_progress`, and a retry after an unrecorded outcome returns `409 idempotency_outcome_unknown`.", - "schema": { - "type": "string", - "minLength": 1, - "maxLength": 255 - } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PreviewMessageTemplateRequest" + } + } + } + }, "responses": { "200": { - "description": "OK", + "description": "Rendered", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ResumeCampaignResponse" + "$ref": "#/components/schemas/PreviewProjectTemplateResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -46374,6 +58394,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -46395,6 +58426,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -46416,6 +58458,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -46437,44 +58490,16 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } - } - }, - "409": { - "description": "`state_conflict`: The campaign is not paused, or it has no active delivery run to resume. The Idempotency-Key already succeeded (`idempotency_completed`), was used for a different request (`idempotency_conflict`), belongs to a request that is still running (`idempotency_in_progress`, with Retry-After), or belongs to a request whose outcome was not recorded (`idempotency_outcome_unknown`)", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "conflict_error", - "code": "state_conflict", - "message": "The request conflicts with the resource state.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#state-conflict" - } - } }, "headers": { - "Retry-After": { - "description": "Seconds to wait before retrying `idempotency_in_progress`.", - "schema": { - "type": "integer", - "minimum": 0 - } + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" }, - "Idempotent-Replayed": { - "description": "`true` when this response reports the recorded outcome of an earlier request with the same Idempotency-Key.", - "schema": { - "type": "string", - "enum": [ - "true" - ] - } + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -46497,28 +58522,40 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "campaigns:manage" - } + "content": "templates:read" + }, + "description": "**Required scope:** `templates:read`" } }, - "/messaging/projects/{projectSlug}/campaigns/{id}/stop": { + "/messaging/projects/{projectSlug}/templates/{id}/submit": { "post": { - "operationId": "stopCampaign", + "operationId": "submitProjectTemplateToMeta", "tags": [ - "Campaigns" + "Templates" ], - "summary": "Stop a campaign", - "description": "**Required scope:** `campaigns:manage`\n\nCancels a draft, running, or paused campaign. Recipients not yet sent are not sent. The campaign moves to `cancelling` and then to `cancelled` once sends already in progress finish; a campaign with no active delivery run is `cancelled` immediately.", + "summary": "Submit a project template to Meta for approval on a Cloud-API session", + "description": "**Required scope:** `templates:manage`\n\nRenders the canonical template into a Meta create-template payload and submits it through the given cloud_api session. Records a per-session approval pointer (cloudLinks) and returns the updated template. The Meta review verdict arrives later via webhook.", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "campaigns:manage", + "x-required-scope": "templates:manage", "parameters": [ { "schema": { @@ -46535,28 +58572,37 @@ "required": true, "name": "id", "in": "path" - }, - { - "name": "Idempotency-Key", - "in": "header", - "required": false, - "description": "Unique key, 1 to 255 bytes of UTF-8, that makes this write safe to retry for 24 hours. A retry with the same key, credential, `Polymorfa-Version`, and request never runs the write again: it returns `409 idempotency_completed` when the original succeeded, or the original server error, with `Idempotent-Replayed: true`. Reusing the key for a different request returns `409 idempotency_conflict`; a retry while the original is still running returns `409 idempotency_in_progress`, and a retry after an unrecorded outcome returns `409 idempotency_outcome_unknown`.", - "schema": { - "type": "string", - "minLength": 1, - "maxLength": 255 - } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/SubmitMessageTemplateRequest" + } + } + } + }, "responses": { "200": { - "description": "OK", + "description": "Submitted", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/StopCampaignResponse" + "$ref": "#/components/schemas/SubmitProjectTemplateResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -46578,6 +58624,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -46599,6 +58656,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -46620,6 +58688,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -46641,44 +58720,16 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } - } - }, - "409": { - "description": "`state_conflict`: The campaign is already finished or cancelled. The Idempotency-Key already succeeded (`idempotency_completed`), was used for a different request (`idempotency_conflict`), belongs to a request that is still running (`idempotency_in_progress`, with Retry-After), or belongs to a request whose outcome was not recorded (`idempotency_outcome_unknown`)", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "conflict_error", - "code": "state_conflict", - "message": "The request conflicts with the resource state.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#state-conflict" - } - } }, "headers": { - "Retry-After": { - "description": "Seconds to wait before retrying `idempotency_in_progress`.", - "schema": { - "type": "integer", - "minimum": 0 - } + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" }, - "Idempotent-Replayed": { - "description": "`true` when this response reports the recorded outcome of an earlier request with the same Idempotency-Key.", - "schema": { - "type": "string", - "enum": [ - "true" - ] - } + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -46701,28 +58752,38 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "campaigns:manage" + "content": "templates:manage" } } }, - "/messaging/projects/{projectSlug}/campaigns/{id}/reschedule": { - "post": { - "operationId": "rescheduleCampaign", + "/messaging/projects/{projectSlug}/campaigns": { + "get": { + "operationId": "listCampaigns", "tags": [ "Campaigns" ], - "summary": "Reschedule a campaign", - "description": "**Required scope:** `campaigns:manage`\n\nMoves the start of a launched campaign that is still waiting for it. Set `scheduledAt` to a later or earlier time, or to `null` to start now. The response returns the campaign with its new `scheduledAt`. Sending the current start time again changes nothing. A campaign that has started, finished, is being stopped, or was never launched is refused with `409`; launch a draft with `scheduledAt` instead.", + "summary": "List campaigns", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "campaigns:manage", + "x-required-scope": "campaigns:read", "parameters": [ { "schema": { @@ -46731,46 +58792,28 @@ "required": true, "name": "projectSlug", "in": "path" - }, - { - "schema": { - "type": "string" - }, - "required": true, - "name": "id", - "in": "path" - }, - { - "name": "Idempotency-Key", - "in": "header", - "required": false, - "description": "Unique key, 1 to 255 bytes of UTF-8, that makes this write safe to retry for 24 hours. A retry with the same key, credential, `Polymorfa-Version`, and request never runs the write again: it returns `409 idempotency_completed` when the original succeeded, or the original server error, with `Idempotent-Replayed: true`. Reusing the key for a different request returns `409 idempotency_conflict`; a retry while the original is still running returns `409 idempotency_in_progress`, and a retry after an unrecorded outcome returns `409 idempotency_outcome_unknown`.", - "schema": { - "type": "string", - "minLength": 1, - "maxLength": 255 - } } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/RescheduleCampaignRequest" - } - } - } - }, "responses": { "200": { "description": "OK", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/RescheduleCampaignResponse" + "$ref": "#/components/schemas/ListCampaignsResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -46792,6 +58835,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -46813,6 +58867,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -46834,6 +58899,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -46855,44 +58931,16 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } - } - }, - "409": { - "description": "`state_conflict`: The campaign has already started, is finished or being stopped, or was never launched. The Idempotency-Key already succeeded (`idempotency_completed`), was used for a different request (`idempotency_conflict`), belongs to a request that is still running (`idempotency_in_progress`, with Retry-After), or belongs to a request whose outcome was not recorded (`idempotency_outcome_unknown`)", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicError" - }, - "example": { - "error": { - "type": "conflict_error", - "code": "state_conflict", - "message": "The request conflicts with the resource state.", - "param": null, - "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" - }, - "data": null, - "docs": "https://docs.polymorfa.com/api/errors#state-conflict" - } - } }, "headers": { - "Retry-After": { - "description": "Seconds to wait before retrying `idempotency_in_progress`.", - "schema": { - "type": "integer", - "minimum": 0 - } + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" }, - "Idempotent-Replayed": { - "description": "`true` when this response reports the recorded outcome of an earlier request with the same Idempotency-Key.", - "schema": { - "type": "string", - "enum": [ - "true" - ] - } + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -46915,21 +58963,32 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "campaigns:manage" - } - } - }, - "/messaging/projects/{projectSlug}/campaigns/{id}/requeue": { + "content": "campaigns:read" + }, + "description": "**Required scope:** `campaigns:read`" + }, "post": { - "operationId": "requeueCampaign", + "operationId": "createCampaign", "tags": [ "Campaigns" ], - "summary": "Re-queue failed recipients", + "summary": "Create a campaign", + "description": "**Required scope:** `campaigns:manage`\n\nCreates a draft campaign on any plan. `senderConfig.sessionIds` may name numbers that are not yet on a plan that includes Campaigns; launch checks the selection. It is refused with `403 bansafe_org_suspended` while the team is suspended and `400` when `senderConfig.sessionIds` is malformed or a per-number cap is above its safety ceiling.", "security": [ { "BearerAuth": [] @@ -46946,19 +59005,22 @@ "in": "path" }, { + "name": "Idempotency-Key", + "in": "header", + "required": false, + "description": "Unique key, 1 to 255 bytes of UTF-8, that makes this write safe to retry for 24 hours. A retry with the same key, credential, `Polymorfa-Version`, and request never runs the write again: it returns `409 idempotency_completed` when the original succeeded, or the original server error, with `Idempotent-Replayed: true`. Reusing the key for a different request returns `409 idempotency_conflict`; a retry while the original is still running returns `409 idempotency_in_progress`, and a retry after an unrecorded outcome returns `409 idempotency_outcome_unknown`.", "schema": { - "type": "string" - }, - "required": true, - "name": "id", - "in": "path" + "type": "string", + "minLength": 1, + "maxLength": 255 + } } ], "requestBody": { "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/RequeueCampaignRequest" + "$ref": "#/components/schemas/CreateCampaignRequest" } } } @@ -46969,9 +59031,20 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/RequeueCampaignResponse" + "$ref": "#/components/schemas/CreateCampaignResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -46993,6 +59066,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -47014,6 +59098,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -47035,6 +59130,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -47056,6 +59162,64 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "409": { + "description": "The Idempotency-Key already succeeded (`idempotency_completed`), was used for a different request (`idempotency_conflict`), belongs to a request that is still running (`idempotency_in_progress`, with Retry-After), or belongs to a request whose outcome was not recorded (`idempotency_outcome_unknown`)", + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying `idempotency_in_progress`.", + "schema": { + "type": "integer", + "minimum": 0 + } + }, + "Idempotent-Replayed": { + "description": "`true` when this response reports the recorded outcome of an earlier request with the same Idempotency-Key.", + "schema": { + "type": "string", + "enum": [ + "true" + ] + } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + }, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "conflict_error", + "code": "idempotency_conflict", + "message": "This Idempotency-Key was already used with a different request. Use a new key for a different request.", + "param": null + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#idempotency-conflict" + } + } } }, "500": { @@ -47077,23 +59241,32 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { "content": "campaigns:manage" - }, - "description": "**Required scope:** `campaigns:manage`" + } } }, - "/messaging/projects/{projectSlug}/campaigns/{id}/recipients": { + "/messaging/projects/{projectSlug}/campaigns/{id}": { "get": { - "operationId": "listProjectCampaignRecipients", + "operationId": "getCampaign", "tags": [ "Campaigns" ], - "summary": "List campaign recipients", - "description": "**Required scope:** `campaigns:read`\n\nLists a campaign's recipients in queue order with cursor pagination. Filter by `status` to find, for example, recipients skipped because they opted out.", + "summary": "Get a campaign", "security": [ { "BearerAuth": [] @@ -47116,43 +59289,6 @@ "required": true, "name": "id", "in": "path" - }, - { - "schema": { - "type": "string", - "enum": [ - "queued", - "sending", - "sent", - "delivered", - "read", - "failed", - "skipped" - ] - }, - "required": false, - "name": "status", - "in": "query" - }, - { - "schema": { - "type": "string", - "maxLength": 256 - }, - "required": false, - "name": "cursor", - "in": "query" - }, - { - "schema": { - "type": "integer", - "minimum": 1, - "maximum": 100, - "description": "Page size, default 25." - }, - "required": false, - "name": "limit", - "in": "query" } ], "responses": { @@ -47161,9 +59297,20 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ListCampaignRecipientsResponse" + "$ref": "#/components/schemas/GetCampaignResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -47185,6 +59332,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -47206,6 +59364,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -47227,6 +59396,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -47248,6 +59428,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -47269,20 +59460,32 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { "content": "campaigns:read" - } + }, + "description": "**Required scope:** `campaigns:read`" }, - "post": { - "operationId": "addProjectCampaignRecipients", + "patch": { + "operationId": "updateProjectCampaign", "tags": [ "Campaigns" ], - "summary": "Add recipients to a draft campaign", - "description": "**Required scope:** `campaigns:manage`\n\nAdds up to 1,000 recipients to a campaign that has not started sending. A phone already on the campaign is skipped. Invalid entries are reported and not added.", + "summary": "Update a campaign", + "description": "**Required scope:** `campaigns:manage`\n\nUpdates the fields you send and leaves the rest unchanged. On an unlaunched draft, a new `senderConfig` passes the same checks as create, on any plan; launch checks the selection against live plans. Once the campaign has launched, including a scheduled launch that has not started, a new `senderConfig` must pass the launch checks and can answer `402 campaigns_not_entitled` or `400 invalid_parameter`. `recipientListId` and a different `scheduledAt` are accepted only while the campaign is an unlaunched draft; afterwards they answer `409`. A sender change to a draft that launches while the change is being saved also answers `409`. `sendWindow` changes only while draft or paused; a paused edit answers `409` while sends are in progress. A non-null `scheduledAt` must be integer Unix milliseconds within the JavaScript Date range.", "security": [ { "BearerAuth": [] @@ -47300,7 +59503,8 @@ }, { "schema": { - "type": "string" + "type": "string", + "format": "uuid" }, "required": true, "name": "id", @@ -47308,10 +59512,11 @@ } ], "requestBody": { + "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AddCampaignRecipientsRequest" + "$ref": "#/components/schemas/UpdateCampaignRequest" } } } @@ -47322,9 +59527,20 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/AddCampaignRecipientsResponse" + "$ref": "#/components/schemas/UpdateCampaignResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -47346,6 +59562,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -47367,6 +59594,49 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "402": { + "description": "`campaigns_not_entitled`: a new sender selection on a launched campaign names a number whose plan does not include Campaigns", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "permission_error", + "code": "campaigns_not_entitled", + "message": "This number’s plan does not include Campaigns.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#campaigns-not-entitled" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -47388,6 +59658,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -47409,10 +59690,21 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { - "description": "The campaign has started sending and no longer accepts recipients", + "description": "`state_conflict`: the campaign has launched and the audience or start time can no longer change, it launched while its sending numbers were being changed, or its send window cannot change in the current state or while sends are in progress", "content": { "application/json": { "schema": { @@ -47430,6 +59722,17 @@ "docs": "https://docs.polymorfa.com/api/errors#state-conflict" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -47451,6 +59754,49 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "503": { + "description": "`service_unavailable`: number eligibility for a launched campaign's new sender selection could not be checked. Nothing was changed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "service_unavailable", + "message": "A required service is unavailable. Check completion before repeating a write.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -47459,38 +59805,57 @@ } } }, - "/messaging/quicklinks": { - "post": { - "operationId": "createQuickLink", + "/messaging/projects/{projectSlug}/campaigns/{id}/analytics": { + "get": { + "operationId": "getCampaignAnalytics", "tags": [ - "QuickLink" + "Campaigns" ], - "summary": "Create a QuickLink", - "description": "**Required scope:** `quicklink:manage`\n\nCreates a persistent hosted link that connects one WhatsApp number to a new session in the project.", + "summary": "Campaign analytics (funnel, response timing, retries)", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "quicklink:manage", - "requestBody": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/CreateQuickLinkRequest" - } - } + "x-required-scope": "campaigns:read", + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "projectSlug", + "in": "path" + }, + { + "schema": { + "type": "string" + }, + "required": true, + "name": "id", + "in": "path" } - }, + ], "responses": { - "201": { - "description": "QuickLink created", + "200": { + "description": "OK", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/QuickLinkResponse" + "$ref": "#/components/schemas/CampaignAnalyticsResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -47512,6 +59877,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -47533,6 +59909,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -47554,6 +59941,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -47575,6 +59973,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -47596,57 +60005,98 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "quicklink:manage" - } + "content": "campaigns:read" + }, + "description": "**Required scope:** `campaigns:read`" } }, - "/messaging/quicklinks/availability": { - "get": { - "operationId": "getHybridQuickLinkAvailability", + "/messaging/projects/{projectSlug}/campaigns/{id}/launch": { + "post": { + "operationId": "launchCampaign", "tags": [ - "QuickLink" + "Campaigns" ], - "summary": "Read connection addition availability", + "summary": "Launch a campaign", + "description": "**Required scope:** `campaigns:manage`\n\nStarts a draft campaign. It begins sending at `scheduledAt`, or right away when no start time is set. Launch checks the saved sender selection against live plans: `402 campaigns_not_entitled` when a selected number is missing or its plan does not include Campaigns, or when an unselected campaign's project has no such number; `400 invalid_parameter` on `senderConfig.sessionIds` when a selected number is not connected or the selection mixes sending types; `400 campaign_throughput_capped` when a requested throughput is above its ceiling. The response returns the campaign as it was when the launch was accepted.", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "quicklink:manage", + "x-required-scope": "campaigns:manage", "parameters": [ { "schema": { - "type": "string", - "format": "uuid" + "type": "string" }, "required": true, - "name": "projectId", - "in": "query" + "name": "projectSlug", + "in": "path" }, { "schema": { - "type": "string", - "minLength": 1, - "maxLength": 128 + "type": "string" }, "required": true, - "name": "session", - "in": "query" + "name": "id", + "in": "path" + }, + { + "name": "Idempotency-Key", + "in": "header", + "required": false, + "description": "Unique key, 1 to 255 bytes of UTF-8, that makes this write safe to retry for 24 hours. A retry with the same key, credential, `Polymorfa-Version`, and request never runs the write again: it returns `409 idempotency_completed` when the original succeeded, or the original server error, with `Idempotent-Replayed: true`. Reusing the key for a different request returns `409 idempotency_conflict`; a retry while the original is still running returns `409 idempotency_in_progress`, and a retry after an unrecorded outcome returns `409 idempotency_outcome_unknown`.", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 255 + } } ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/LaunchCampaignRequest" + } + } + } + }, "responses": { "200": { - "description": "Connection state and permitted addition", + "description": "OK", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/HybridQuickLinkAvailabilityResponse" + "$ref": "#/components/schemas/LaunchCampaignResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -47668,6 +60118,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -47689,6 +60150,49 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "402": { + "description": "`campaigns_not_entitled`: a selected number is missing or its plan does not include Campaigns, or the project has no number on such a plan. Drafts are saved on every plan.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "permission_error", + "code": "campaigns_not_entitled", + "message": "This number’s plan does not include Campaigns.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#campaigns-not-entitled" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -47710,6 +60214,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -47731,6 +60246,65 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "409": { + "description": "`state_conflict`: The campaign is not a draft, it is already launched and waiting for its start time, or its sending numbers changed during the launch. The Idempotency-Key already succeeded (`idempotency_completed`), was used for a different request (`idempotency_conflict`), belongs to a request that is still running (`idempotency_in_progress`, with Retry-After), or belongs to a request whose outcome was not recorded (`idempotency_outcome_unknown`)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "conflict_error", + "code": "state_conflict", + "message": "The request conflicts with the resource state.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#state-conflict" + } + } + }, + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying `idempotency_in_progress`.", + "schema": { + "type": "integer", + "minimum": 0 + } + }, + "Idempotent-Replayed": { + "description": "`true` when this response reports the recorded outcome of an earlier request with the same Idempotency-Key.", + "schema": { + "type": "string", + "enum": [ + "true" + ] + } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -47752,29 +60326,80 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } - } - }, - "x-mint": { - "content": "quicklink:manage" - }, - "description": "**Required scope:** `quicklink:manage`" + }, + "503": { + "description": "`service_unavailable`: the selected numbers' plans could not be checked; retry", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "service_unavailable", + "message": "A required service is unavailable. Check completion before repeating a write.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + } + }, + "x-mint": { + "content": "campaigns:manage" + } } }, - "/messaging/quicklinks/{id}": { - "get": { - "operationId": "getQuickLink", + "/messaging/projects/{projectSlug}/campaigns/{id}/pause": { + "post": { + "operationId": "pauseCampaign", "tags": [ - "QuickLink" + "Campaigns" ], - "summary": "Read QuickLink status", + "summary": "Pause a campaign", + "description": "**Required scope:** `campaigns:manage`\n\nPauses a running campaign. Sends already in progress can still finish.", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "quicklink:manage", + "x-required-scope": "campaigns:manage", "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "projectSlug", + "in": "path" + }, { "schema": { "type": "string" @@ -47782,17 +60407,39 @@ "required": true, "name": "id", "in": "path" + }, + { + "name": "Idempotency-Key", + "in": "header", + "required": false, + "description": "Unique key, 1 to 255 bytes of UTF-8, that makes this write safe to retry for 24 hours. A retry with the same key, credential, `Polymorfa-Version`, and request never runs the write again: it returns `409 idempotency_completed` when the original succeeded, or the original server error, with `Idempotent-Replayed: true`. Reusing the key for a different request returns `409 idempotency_conflict`; a retry while the original is still running returns `409 idempotency_in_progress`, and a retry after an unrecorded outcome returns `409 idempotency_outcome_unknown`.", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 255 + } } ], "responses": { "200": { - "description": "Current QuickLink status", + "description": "OK", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/QuickLinkStatusResponse" + "$ref": "#/components/schemas/PauseCampaignResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -47814,6 +60461,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -47835,6 +60493,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -47856,6 +60525,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -47877,6 +60557,65 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "409": { + "description": "`state_conflict`: The campaign is not running, or it has no active delivery run to pause. The Idempotency-Key already succeeded (`idempotency_completed`), was used for a different request (`idempotency_conflict`), belongs to a request that is still running (`idempotency_in_progress`, with Retry-After), or belongs to a request whose outcome was not recorded (`idempotency_outcome_unknown`)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "conflict_error", + "code": "state_conflict", + "message": "The request conflicts with the resource state.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#state-conflict" + } + } + }, + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying `idempotency_in_progress`.", + "schema": { + "type": "integer", + "minimum": 0 + } + }, + "Idempotent-Replayed": { + "description": "`true` when this response reports the recorded outcome of an earlier request with the same Idempotency-Key.", + "schema": { + "type": "string", + "enum": [ + "true" + ] + } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -47898,28 +60637,48 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "quicklink:manage" - }, - "description": "**Required scope:** `quicklink:manage`" - }, - "delete": { - "operationId": "cancelQuickLink", + "content": "campaigns:manage" + } + } + }, + "/messaging/projects/{projectSlug}/campaigns/{id}/resume": { + "post": { + "operationId": "resumeCampaign", "tags": [ - "QuickLink" + "Campaigns" ], - "summary": "Cancel a QuickLink", - "description": "**Required scope:** `quicklink:manage`\n\nCancels pending onboarding. Initial Number cleanup is accepted asynchronously and completes only after runtime teardown. A supplementary link cancels only its pending addition and preserves the Number. Accepted provider work must finish before cancellation.", + "summary": "Resume a campaign", + "description": "**Required scope:** `campaigns:manage`\n\nResumes a paused campaign. Its remaining recipients are sent after the same checks as before the pause.", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "quicklink:manage", + "x-required-scope": "campaigns:manage", "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "projectSlug", + "in": "path" + }, { "schema": { "type": "string" @@ -47927,56 +60686,38 @@ "required": true, "name": "id", "in": "path" + }, + { + "name": "Idempotency-Key", + "in": "header", + "required": false, + "description": "Unique key, 1 to 255 bytes of UTF-8, that makes this write safe to retry for 24 hours. A retry with the same key, credential, `Polymorfa-Version`, and request never runs the write again: it returns `409 idempotency_completed` when the original succeeded, or the original server error, with `Idempotent-Replayed: true`. Reusing the key for a different request returns `409 idempotency_conflict`; a retry while the original is still running returns `409 idempotency_in_progress`, and a retry after an unrecorded outcome returns `409 idempotency_outcome_unknown`.", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 255 + } } ], "responses": { "200": { - "description": "Cancelled", + "description": "OK", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "message": { - "type": "string" - } - }, - "required": [ - "success", - "message" - ] + "$ref": "#/components/schemas/ResumeCampaignResponse" } } - } - }, - "202": { - "description": "QuickLink invalidated; Number cleanup pending", - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "message": { - "type": "string" - } - }, - "required": [ - "success", - "message" - ] - } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, @@ -47999,6 +60740,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -48020,6 +60772,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -48041,6 +60804,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -48062,6 +60836,65 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "409": { + "description": "`state_conflict`: The campaign is not paused, or it has no active delivery run to resume. The Idempotency-Key already succeeded (`idempotency_completed`), was used for a different request (`idempotency_conflict`), belongs to a request that is still running (`idempotency_in_progress`, with Retry-After), or belongs to a request whose outcome was not recorded (`idempotency_outcome_unknown`)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "conflict_error", + "code": "state_conflict", + "message": "The request conflicts with the resource state.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#state-conflict" + } + } + }, + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying `idempotency_in_progress`.", + "schema": { + "type": "integer", + "minimum": 0 + } + }, + "Idempotent-Replayed": { + "description": "`true` when this response reports the recorded outcome of an earlier request with the same Idempotency-Key.", + "schema": { + "type": "string", + "enum": [ + "true" + ] + } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -48083,156 +60916,2924 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, "x-mint": { - "content": "quicklink:manage" + "content": "campaigns:manage" } } }, - "/quicklink/{token}/phone": { + "/messaging/projects/{projectSlug}/campaigns/{id}/stop": { "post": { - "operationId": "confirmQuickLinkPhone", + "operationId": "stopCampaign", "tags": [ - "QuickLink" + "Campaigns" ], - "security": [], - "summary": "Confirm the number for this invitation", + "summary": "Stop a campaign", + "description": "**Required scope:** `campaigns:manage`\n\nCancels a draft, running, or paused campaign. Recipients not yet sent are not sent. The campaign moves to `cancelling` and then to `cancelled` once sends already in progress finish; a campaign with no active delivery run is `cancelled` immediately.", + "security": [ + { + "BearerAuth": [] + } + ], + "x-required-scope": "campaigns:manage", "parameters": [ { "schema": { - "type": "string", - "description": "Opaque QuickLink token from the hosted URL." + "type": "string" }, "required": true, - "name": "token", + "name": "projectSlug", "in": "path" - } - ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "phone": { - "type": "string", - "maxLength": 32 - }, - "noNumber": { - "type": "boolean", - "enum": [ - true - ] - }, - "whatsappPhone": { - "type": "string", - "maxLength": 32 - } - }, - "additionalProperties": false - } + }, + { + "schema": { + "type": "string" + }, + "required": true, + "name": "id", + "in": "path" + }, + { + "name": "Idempotency-Key", + "in": "header", + "required": false, + "description": "Unique key, 1 to 255 bytes of UTF-8, that makes this write safe to retry for 24 hours. A retry with the same key, credential, `Polymorfa-Version`, and request never runs the write again: it returns `409 idempotency_completed` when the original succeeded, or the original server error, with `Idempotent-Replayed: true`. Reusing the key for a different request returns `409 idempotency_conflict`; a retry while the original is still running returns `409 idempotency_in_progress`, and a retry after an unrecorded outcome returns `409 idempotency_outcome_unknown`.", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 255 } } - }, + ], "responses": { "200": { - "description": "Phone confirmed", + "description": "OK", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "data": { - "type": "object", - "properties": { - "confirmed": { - "type": "boolean" - } - }, - "required": [ - "confirmed" - ] - } - }, - "required": [ - "data" - ] + "$ref": "#/components/schemas/StopCampaignResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { - "description": "QuickLink request failed", + "description": "Bad request — request body or params failed validation", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "invalid_parameter", + "message": "Correct the invalid request field.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } - } - }, - "403": { - "description": "QuickLink request failed", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" - } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, - "404": { - "description": "QuickLink request failed", + "401": { + "description": "Missing or invalid authentication token", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "authentication_error", + "code": "invalid_credential", + "message": "Supply a valid credential.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, - "409": { - "description": "QuickLink request failed", + "403": { + "description": "Authenticated but not permitted (missing scope or rule violation)", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "permission_error", + "code": "permission_denied", + "message": "The credential does not have access to this resource.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, - "422": { - "description": "QuickLink request failed", + "404": { + "description": "Resource not found", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "resource_not_found", + "message": "The resource is unavailable to this caller.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, - "429": { - "description": "QuickLink request failed", + "409": { + "description": "`state_conflict`: The campaign is already finished or cancelled. The Idempotency-Key already succeeded (`idempotency_completed`), was used for a different request (`idempotency_conflict`), belongs to a request that is still running (`idempotency_in_progress`, with Retry-After), or belongs to a request whose outcome was not recorded (`idempotency_outcome_unknown`)", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "conflict_error", + "code": "state_conflict", + "message": "The request conflicts with the resource state.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#state-conflict" } } - } - }, - "500": { - "description": "QuickLink request failed", - "content": { - "application/json": { + }, + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying `idempotency_in_progress`.", "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "type": "integer", + "minimum": 0 } - } - } + }, + "Idempotent-Replayed": { + "description": "`true` when this response reports the recorded outcome of an earlier request with the same Idempotency-Key.", + "schema": { + "type": "string", + "enum": [ + "true" + ] + } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "internal_error", + "message": "An unexpected service failure occurred. Keep the request ID for support.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#internal-error" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + } + }, + "x-mint": { + "content": "campaigns:manage" + } + } + }, + "/messaging/projects/{projectSlug}/campaigns/{id}/reschedule": { + "post": { + "operationId": "rescheduleCampaign", + "tags": [ + "Campaigns" + ], + "summary": "Reschedule a campaign", + "description": "**Required scope:** `campaigns:manage`\n\nMoves the start of a launched campaign that is still waiting for it. Set `scheduledAt` to a later or earlier time, or to `null` to start now. The response returns the campaign with its new `scheduledAt`. Sending the current start time again changes nothing. A campaign that has started, finished, is being stopped, or was never launched is refused with `409`; launch a draft with `scheduledAt` instead.", + "security": [ + { + "BearerAuth": [] + } + ], + "x-required-scope": "campaigns:manage", + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "projectSlug", + "in": "path" + }, + { + "schema": { + "type": "string" + }, + "required": true, + "name": "id", + "in": "path" + }, + { + "name": "Idempotency-Key", + "in": "header", + "required": false, + "description": "Unique key, 1 to 255 bytes of UTF-8, that makes this write safe to retry for 24 hours. A retry with the same key, credential, `Polymorfa-Version`, and request never runs the write again: it returns `409 idempotency_completed` when the original succeeded, or the original server error, with `Idempotent-Replayed: true`. Reusing the key for a different request returns `409 idempotency_conflict`; a retry while the original is still running returns `409 idempotency_in_progress`, and a retry after an unrecorded outcome returns `409 idempotency_outcome_unknown`.", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 255 + } + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RescheduleCampaignRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RescheduleCampaignResponse" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "400": { + "description": "Bad request — request body or params failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "invalid_parameter", + "message": "Correct the invalid request field.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "401": { + "description": "Missing or invalid authentication token", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "authentication_error", + "code": "invalid_credential", + "message": "Supply a valid credential.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "403": { + "description": "Authenticated but not permitted (missing scope or rule violation)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "permission_error", + "code": "permission_denied", + "message": "The credential does not have access to this resource.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#permission-denied" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "404": { + "description": "Resource not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "resource_not_found", + "message": "The resource is unavailable to this caller.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "409": { + "description": "`state_conflict`: The campaign has already started, is finished or being stopped, or was never launched. The Idempotency-Key already succeeded (`idempotency_completed`), was used for a different request (`idempotency_conflict`), belongs to a request that is still running (`idempotency_in_progress`, with Retry-After), or belongs to a request whose outcome was not recorded (`idempotency_outcome_unknown`)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "conflict_error", + "code": "state_conflict", + "message": "The request conflicts with the resource state.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#state-conflict" + } + } + }, + "headers": { + "Retry-After": { + "description": "Seconds to wait before retrying `idempotency_in_progress`.", + "schema": { + "type": "integer", + "minimum": 0 + } + }, + "Idempotent-Replayed": { + "description": "`true` when this response reports the recorded outcome of an earlier request with the same Idempotency-Key.", + "schema": { + "type": "string", + "enum": [ + "true" + ] + } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "internal_error", + "message": "An unexpected service failure occurred. Keep the request ID for support.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#internal-error" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + } + }, + "x-mint": { + "content": "campaigns:manage" + } + } + }, + "/messaging/projects/{projectSlug}/campaigns/{id}/requeue": { + "post": { + "operationId": "requeueCampaign", + "tags": [ + "Campaigns" + ], + "summary": "Re-queue failed recipients", + "security": [ + { + "BearerAuth": [] + } + ], + "x-required-scope": "campaigns:manage", + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "projectSlug", + "in": "path" + }, + { + "schema": { + "type": "string" + }, + "required": true, + "name": "id", + "in": "path" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequeueCampaignRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/RequeueCampaignResponse" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "400": { + "description": "Bad request — request body or params failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "invalid_parameter", + "message": "Correct the invalid request field.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "401": { + "description": "Missing or invalid authentication token", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "authentication_error", + "code": "invalid_credential", + "message": "Supply a valid credential.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "403": { + "description": "Authenticated but not permitted (missing scope or rule violation)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "permission_error", + "code": "permission_denied", + "message": "The credential does not have access to this resource.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#permission-denied" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "404": { + "description": "Resource not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "resource_not_found", + "message": "The resource is unavailable to this caller.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "internal_error", + "message": "An unexpected service failure occurred. Keep the request ID for support.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#internal-error" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + } + }, + "x-mint": { + "content": "campaigns:manage" + }, + "description": "**Required scope:** `campaigns:manage`" + } + }, + "/messaging/projects/{projectSlug}/campaigns/{id}/recipients": { + "get": { + "operationId": "listProjectCampaignRecipients", + "tags": [ + "Campaigns" + ], + "summary": "List campaign recipients", + "description": "**Required scope:** `campaigns:read`\n\nLists a campaign's recipients in queue order with cursor pagination. Filter by `status` to find, for example, recipients skipped because they opted out.", + "security": [ + { + "BearerAuth": [] + } + ], + "x-required-scope": "campaigns:read", + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "projectSlug", + "in": "path" + }, + { + "schema": { + "type": "string" + }, + "required": true, + "name": "id", + "in": "path" + }, + { + "schema": { + "type": "string", + "enum": [ + "queued", + "sending", + "sent", + "delivered", + "read", + "failed", + "skipped" + ] + }, + "required": false, + "name": "status", + "in": "query" + }, + { + "schema": { + "type": "string", + "maxLength": 256 + }, + "required": false, + "name": "cursor", + "in": "query" + }, + { + "schema": { + "type": "integer", + "minimum": 1, + "maximum": 100, + "description": "Page size, default 25." + }, + "required": false, + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ListCampaignRecipientsResponse" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "400": { + "description": "Bad request — request body or params failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "invalid_parameter", + "message": "Correct the invalid request field.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "401": { + "description": "Missing or invalid authentication token", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "authentication_error", + "code": "invalid_credential", + "message": "Supply a valid credential.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "403": { + "description": "Authenticated but not permitted (missing scope or rule violation)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "permission_error", + "code": "permission_denied", + "message": "The credential does not have access to this resource.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#permission-denied" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "404": { + "description": "Resource not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "resource_not_found", + "message": "The resource is unavailable to this caller.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "internal_error", + "message": "An unexpected service failure occurred. Keep the request ID for support.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#internal-error" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + } + }, + "x-mint": { + "content": "campaigns:read" + } + }, + "post": { + "operationId": "addProjectCampaignRecipients", + "tags": [ + "Campaigns" + ], + "summary": "Add recipients to a draft campaign", + "description": "**Required scope:** `campaigns:manage`\n\nAdds up to 1,000 recipients to a campaign that has not started sending. A phone already on the campaign is skipped. Invalid entries are reported and not added.", + "security": [ + { + "BearerAuth": [] + } + ], + "x-required-scope": "campaigns:manage", + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "projectSlug", + "in": "path" + }, + { + "schema": { + "type": "string" + }, + "required": true, + "name": "id", + "in": "path" + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AddCampaignRecipientsRequest" + } + } + } + }, + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AddCampaignRecipientsResponse" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "400": { + "description": "Bad request — request body or params failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "invalid_parameter", + "message": "Correct the invalid request field.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "401": { + "description": "Missing or invalid authentication token", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "authentication_error", + "code": "invalid_credential", + "message": "Supply a valid credential.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "403": { + "description": "Authenticated but not permitted (missing scope or rule violation)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "permission_error", + "code": "permission_denied", + "message": "The credential does not have access to this resource.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#permission-denied" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "404": { + "description": "Resource not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "resource_not_found", + "message": "The resource is unavailable to this caller.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "409": { + "description": "The campaign has started sending and no longer accepts recipients", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "conflict_error", + "code": "state_conflict", + "message": "The request conflicts with the resource state.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#state-conflict" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "internal_error", + "message": "An unexpected service failure occurred. Keep the request ID for support.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#internal-error" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + } + }, + "x-mint": { + "content": "campaigns:manage" + } + } + }, + "/messaging/quicklinks": { + "post": { + "operationId": "createQuickLink", + "tags": [ + "QuickLink" + ], + "summary": "Create a QuickLink", + "description": "**Required scope:** `quicklink:manage`\n\nCreates a persistent hosted link that connects one WhatsApp number to a new session in the project.", + "security": [ + { + "BearerAuth": [] + } + ], + "x-required-scope": "quicklink:manage", + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CreateQuickLinkRequest" + } + } + } + }, + "responses": { + "201": { + "description": "QuickLink created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/QuickLinkResponse" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "400": { + "description": "Bad request — request body or params failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "invalid_parameter", + "message": "Correct the invalid request field.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "401": { + "description": "Missing or invalid authentication token", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "authentication_error", + "code": "invalid_credential", + "message": "Supply a valid credential.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "403": { + "description": "Authenticated but not permitted (missing scope or rule violation)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "permission_error", + "code": "permission_denied", + "message": "The credential does not have access to this resource.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#permission-denied" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "404": { + "description": "Resource not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "resource_not_found", + "message": "The resource is unavailable to this caller.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "internal_error", + "message": "An unexpected service failure occurred. Keep the request ID for support.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#internal-error" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + } + }, + "x-mint": { + "content": "quicklink:manage" + } + } + }, + "/messaging/quicklinks/availability": { + "get": { + "operationId": "getHybridQuickLinkAvailability", + "tags": [ + "QuickLink" + ], + "summary": "Read connection addition availability", + "security": [ + { + "BearerAuth": [] + } + ], + "x-required-scope": "quicklink:manage", + "parameters": [ + { + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true, + "name": "projectId", + "in": "query" + }, + { + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 128 + }, + "required": true, + "name": "session", + "in": "query" + } + ], + "responses": { + "200": { + "description": "Connection state and permitted addition", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/HybridQuickLinkAvailabilityResponse" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "400": { + "description": "Bad request — request body or params failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "invalid_parameter", + "message": "Correct the invalid request field.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "401": { + "description": "Missing or invalid authentication token", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "authentication_error", + "code": "invalid_credential", + "message": "Supply a valid credential.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "403": { + "description": "Authenticated but not permitted (missing scope or rule violation)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "permission_error", + "code": "permission_denied", + "message": "The credential does not have access to this resource.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#permission-denied" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "404": { + "description": "Resource not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "resource_not_found", + "message": "The resource is unavailable to this caller.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "internal_error", + "message": "An unexpected service failure occurred. Keep the request ID for support.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#internal-error" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + } + }, + "x-mint": { + "content": "quicklink:manage" + }, + "description": "**Required scope:** `quicklink:manage`" + } + }, + "/messaging/quicklinks/{id}": { + "get": { + "operationId": "getQuickLink", + "tags": [ + "QuickLink" + ], + "summary": "Read QuickLink status", + "security": [ + { + "BearerAuth": [] + } + ], + "x-required-scope": "quicklink:manage", + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "id", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Current QuickLink status", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/QuickLinkStatusResponse" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "400": { + "description": "Bad request — request body or params failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "invalid_parameter", + "message": "Correct the invalid request field.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "401": { + "description": "Missing or invalid authentication token", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "authentication_error", + "code": "invalid_credential", + "message": "Supply a valid credential.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "403": { + "description": "Authenticated but not permitted (missing scope or rule violation)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "permission_error", + "code": "permission_denied", + "message": "The credential does not have access to this resource.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#permission-denied" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "404": { + "description": "Resource not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "resource_not_found", + "message": "The resource is unavailable to this caller.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "internal_error", + "message": "An unexpected service failure occurred. Keep the request ID for support.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#internal-error" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + } + }, + "x-mint": { + "content": "quicklink:manage" + }, + "description": "**Required scope:** `quicklink:manage`" + }, + "delete": { + "operationId": "cancelQuickLink", + "tags": [ + "QuickLink" + ], + "summary": "Cancel a QuickLink", + "description": "**Required scope:** `quicklink:manage`\n\nCancels pending onboarding. Initial Number cleanup is accepted asynchronously and completes only after runtime teardown. Supplementary and reauthorization links cancel only their pending attempts; the existing Number remains. Accepted provider work must finish before cancellation.", + "security": [ + { + "BearerAuth": [] + } + ], + "x-required-scope": "quicklink:manage", + "parameters": [ + { + "schema": { + "type": "string" + }, + "required": true, + "name": "id", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Cancelled", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "message": { + "type": "string" + } + }, + "required": [ + "success", + "message" + ] + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "202": { + "description": "QuickLink invalidated; Number cleanup pending", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, + "message": { + "type": "string" + } + }, + "required": [ + "success", + "message" + ] + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "400": { + "description": "Bad request — request body or params failed validation", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "invalid_parameter", + "message": "Correct the invalid request field.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "401": { + "description": "Missing or invalid authentication token", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "authentication_error", + "code": "invalid_credential", + "message": "Supply a valid credential.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "403": { + "description": "Authenticated but not permitted (missing scope or rule violation)", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "permission_error", + "code": "permission_denied", + "message": "The credential does not have access to this resource.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#permission-denied" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "404": { + "description": "Resource not found", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "resource_not_found", + "message": "The resource is unavailable to this caller.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "internal_error", + "message": "An unexpected service failure occurred. Keep the request ID for support.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#internal-error" + } + } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + } + } + }, + "x-mint": { + "content": "quicklink:manage" + } + } + }, + "/messaging/{session}/cloud-credentials": { + "get": { + "operationId": "getCloudCredentialHealth", + "tags": [ + "Sessions" + ], + "summary": "Read Meta Cloud API credential health", + "description": "**Protocols:** Meta Cloud API\n\n**Required scope:** `sessions:read`\n\nReturns the latest recurring validation of the Number's Meta Cloud API credentials: token validity, required permissions, phone registration and webhook subscription.", + "security": [ + { + "BearerAuth": [] + } + ], + "x-required-scope": "sessions:read", + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 128 + }, + "required": true, + "name": "session", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Credential health", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CloudCredentialHealthResponse" + } + } + } + }, + "400": { + "description": "Request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + } + } + } + }, + "401": { + "description": "Request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + } + } + } + }, + "403": { + "description": "Request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + } + } + } + }, + "404": { + "description": "Request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + } + } + } + }, + "409": { + "description": "Request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + } + } + } + }, + "500": { + "description": "Request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + } + } + } + }, + "503": { + "description": "Request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + } + } + } + } + }, + "x-mint": { + "content": "Meta Cloud API sessions:read" + }, + "x-protocols": [ + "Meta Cloud API" + ] + } + }, + "/messaging/{session}/cloud-credentials/reauthorize": { + "post": { + "operationId": "reauthorizeCloudCredentials", + "tags": [ + "Sessions" + ], + "summary": "Start guided Meta Cloud API reauthorization", + "description": "**Protocols:** Meta Cloud API\n\n**Required scope:** `quicklink:manage`\n\nRequires a project token with `quicklink:manage`. Creates a QuickLink with purpose `reauthorization` that renews the Number's Meta authorization through Embedded Signup. The link is bound to the same Number and phone number and always uses the Meta Cloud API. The Number must be stopped or disconnected; this request never stops it. Cancelling the link before Meta work starts keeps the Number.", + "security": [ + { + "BearerAuth": [] + } + ], + "x-required-scope": "quicklink:manage", + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 128 + }, + "required": true, + "name": "session", + "in": "path" + } + ], + "responses": { + "201": { + "description": "Reauthorization link created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/CloudReauthorizationResponse" + } + } + } + }, + "400": { + "description": "Request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + } + } + } + }, + "401": { + "description": "Request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + } + } + } + }, + "402": { + "description": "Request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + } + } + } + }, + "403": { + "description": "Request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + } + } + } + }, + "404": { + "description": "Request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + } + } + } + }, + "409": { + "description": "Request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + } + } + } + }, + "500": { + "description": "Request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + } + } + } + }, + "501": { + "description": "Request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + } + } + } + }, + "503": { + "description": "Request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + } + } + } + } + }, + "x-mint": { + "content": "Meta Cloud API quicklink:manage" + }, + "x-protocols": [ + "Meta Cloud API" + ] + } + }, + "/quicklink/{token}/phone": { + "post": { + "operationId": "confirmQuickLinkPhone", + "tags": [ + "QuickLink" + ], + "security": [], + "summary": "Confirm the number for this invitation", + "parameters": [ + { + "schema": { + "type": "string", + "description": "Opaque QuickLink token from the hosted URL." + }, + "required": true, + "name": "token", + "in": "path" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "phone": { + "type": "string", + "maxLength": 32 + }, + "noNumber": { + "type": "boolean", + "enum": [ + true + ] + }, + "whatsappPhone": { + "type": "string", + "maxLength": 32 + } + }, + "additionalProperties": false + } + } + } + }, + "responses": { + "200": { + "description": "Phone confirmed", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "confirmed": { + "type": "boolean" + } + }, + "required": [ + "confirmed" + ] + } + }, + "required": [ + "data" + ] + } + } + } + }, + "400": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "403": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "404": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "409": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "422": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "429": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "500": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "503": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + } + } + } + }, + "/quicklink/{token}/phone/reset": { + "post": { + "operationId": "resetQuickLinkPhone", + "tags": [ + "QuickLink" + ], + "security": [], + "summary": "Return to phone entry before connecting", + "parameters": [ + { + "schema": { + "type": "string", + "description": "Opaque QuickLink token from the hosted URL." + }, + "required": true, + "name": "token", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Phone entry restored", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "confirmed": { + "type": "boolean" + } + }, + "required": [ + "confirmed" + ] + } + }, + "required": [ + "data" + ] + } + } + } + }, + "400": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "403": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "404": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "409": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "422": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "429": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "500": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "503": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + } + } + } + }, + "/quicklink/{token}/connection": { + "post": { + "operationId": "selectQuickLinkConnection", + "tags": [ + "QuickLink" + ], + "security": [], + "summary": "Select an allowed connection type", + "parameters": [ + { + "schema": { + "type": "string", + "description": "Opaque QuickLink token from the hosted URL." + }, + "required": true, + "name": "token", + "in": "path" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "connection": { + "type": "string", + "enum": [ + "cloud", + "linked" + ] + } + }, + "required": [ + "connection" + ], + "additionalProperties": false + } + } + } + }, + "responses": { + "200": { + "description": "Selected", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "connection": { + "type": "string", + "enum": [ + "cloud", + "linked" + ] + } + }, + "required": [ + "connection" + ] + } + }, + "required": [ + "data" + ] + } + } + } + }, + "400": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "403": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "404": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "409": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "422": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "429": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "500": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } }, "503": { "description": "QuickLink request failed", @@ -48247,14 +63848,14 @@ } } }, - "/quicklink/{token}/phone/reset": { + "/quicklink/{token}/cloud": { "post": { - "operationId": "resetQuickLinkPhone", + "operationId": "completeQuickLinkCloudSignup", "tags": [ "QuickLink" ], "security": [], - "summary": "Return to phone entry before connecting", + "summary": "Advance Cloud onboarding", "parameters": [ { "schema": { @@ -48266,9 +63867,25 @@ "in": "path" } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "result": { + "$ref": "#/components/schemas/EmbeddedSignupResult" + } + }, + "additionalProperties": false + } + } + } + }, "responses": { "200": { - "description": "Phone entry restored", + "description": "Onboarding progress", "content": { "application/json": { "schema": { @@ -48277,12 +63894,12 @@ "data": { "type": "object", "properties": { - "confirmed": { - "type": "boolean" + "stage": { + "type": "string" } }, "required": [ - "confirmed" + "stage" ] } }, @@ -48376,14 +63993,450 @@ } } }, - "/quicklink/{token}/connection": { - "post": { - "operationId": "selectQuickLinkConnection", + "/quicklink/{token}/history": { + "post": { + "operationId": "chooseQuickLinkHistory", + "tags": [ + "QuickLink" + ], + "security": [], + "summary": "Choose history sharing before connecting", + "parameters": [ + { + "schema": { + "type": "string", + "description": "Opaque QuickLink token from the hosted URL." + }, + "required": true, + "name": "token", + "in": "path" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "historySync": { + "type": "boolean" + } + }, + "required": [ + "historySync" + ], + "additionalProperties": false + } + } + } + }, + "responses": { + "200": { + "description": "History choice recorded", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/QuickLinkState" + } + }, + "required": [ + "data" + ] + } + } + } + }, + "400": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "403": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "404": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "409": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "422": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "429": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "500": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "503": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + } + } + } + }, + "/quicklink/{token}/testing": { + "patch": { + "operationId": "customizeQuickLinkTesting", + "tags": [ + "QuickLink" + ], + "security": [], + "summary": "Customize a simulated QuickLink account", + "parameters": [ + { + "schema": { + "type": "string", + "description": "Opaque QuickLink token from the hosted URL." + }, + "required": true, + "name": "token", + "in": "path" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "profile": { + "type": "object", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 128 + }, + "status": { + "type": "string", + "maxLength": 139 + } + }, + "additionalProperties": false + }, + "accountType": { + "type": "string", + "enum": [ + "personal", + "business" + ] + }, + "replyBehavior": { + "type": "string", + "enum": [ + "off", + "echo" + ] + }, + "failureScenario": { + "type": "string", + "enum": [ + "none", + "reject-send" + ] + }, + "historyFixtureId": { + "type": "string", + "format": "uuid" + } + }, + "additionalProperties": false + } + } + } + }, + "responses": { + "200": { + "description": "Test configuration updated", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/QuickLinkState" + } + }, + "required": [ + "data" + ] + } + } + } + }, + "400": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "403": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "404": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "409": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "422": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "429": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "500": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "503": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + } + } + } + }, + "/quicklink/{token}/testing/start": { + "post": { + "operationId": "startQuickLinkTesting", + "tags": [ + "QuickLink" + ], + "security": [], + "summary": "Start the simulated account reserved by a QuickLink", + "parameters": [ + { + "schema": { + "type": "string", + "description": "Opaque QuickLink token from the hosted URL." + }, + "required": true, + "name": "token", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Current test connection state", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/QuickLinkState" + } + }, + "required": [ + "data" + ] + } + } + } + }, + "400": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "403": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "404": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "409": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "422": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "429": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "500": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, + "503": { + "description": "QuickLink request failed", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + } + } + } + }, + "/quicklink/{token}": { + "get": { + "operationId": "getQuickLinkState", "tags": [ "QuickLink" ], "security": [], - "summary": "Select an allowed connection type", + "summary": "Read QuickLink state", + "description": "Returns the hosted page state for a QuickLink. Public; the token in the URL is the credential. Starts linked-device pairing after an allowed connection type has been selected.", "parameters": [ { "schema": { @@ -48395,51 +64448,16 @@ "in": "path" } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "connection": { - "type": "string", - "enum": [ - "cloud", - "linked" - ] - } - }, - "required": [ - "connection" - ], - "additionalProperties": false - } - } - } - }, "responses": { "200": { - "description": "Selected", + "description": "Current QuickLink state", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { - "type": "object", - "properties": { - "connection": { - "type": "string", - "enum": [ - "cloud", - "linked" - ] - } - }, - "required": [ - "connection" - ] + "$ref": "#/components/schemas/QuickLinkState" } }, "required": [ @@ -48532,14 +64550,15 @@ } } }, - "/quicklink/{token}/cloud": { - "post": { - "operationId": "completeQuickLinkCloudSignup", + "/quicklink/{token}/logo": { + "get": { + "operationId": "getQuickLinkLogo", "tags": [ "QuickLink" ], "security": [], - "summary": "Advance Cloud onboarding", + "summary": "Read the QuickLink page logo", + "description": "Returns the saved logo image for the page. Public; the token in the URL is the credential.", "parameters": [ { "schema": { @@ -48551,45 +64570,14 @@ "in": "path" } ], - "requestBody": { - "required": true, - "content": { - "application/json": { - "schema": { - "type": "object", - "properties": { - "result": { - "$ref": "#/components/schemas/EmbeddedSignupResult" - } - }, - "additionalProperties": false - } - } - } - }, "responses": { "200": { - "description": "Onboarding progress", + "description": "Logo image", "content": { - "application/json": { + "image/png": { "schema": { - "type": "object", - "properties": { - "data": { - "type": "object", - "properties": { - "stage": { - "type": "string" - } - }, - "required": [ - "stage" - ] - } - }, - "required": [ - "data" - ] + "type": "string", + "format": "binary" } } } @@ -48677,14 +64665,14 @@ } } }, - "/quicklink/{token}/history": { + "/quicklink/{token}/code": { "post": { - "operationId": "chooseQuickLinkHistory", + "operationId": "requestQuickLinkCode", "tags": [ "QuickLink" ], "security": [], - "summary": "Choose history sharing before connecting", + "summary": "Request a QuickLink pairing code", "parameters": [ { "schema": { @@ -48697,34 +64685,36 @@ } ], "requestBody": { - "required": true, "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "historySync": { - "type": "boolean" - } - }, - "required": [ - "historySync" - ], - "additionalProperties": false + "$ref": "#/components/schemas/QuickLinkCodeRequest" } } } }, "responses": { "200": { - "description": "History choice recorded", + "description": "Pairing code issued", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/QuickLinkState" + "type": "object", + "properties": { + "code": { + "type": "string" + }, + "phone": { + "type": "string" + } + }, + "required": [ + "code", + "phone" + ] } }, "required": [ @@ -48744,6 +64734,16 @@ } } }, + "402": { + "description": "The team cannot fund pairing", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicQuickLinkError" + } + } + } + }, "403": { "description": "QuickLink request failed", "content": { @@ -48817,14 +64817,15 @@ } } }, - "/quicklink/{token}/testing": { - "patch": { - "operationId": "customizeQuickLinkTesting", + "/quicklink/{token}/confirm": { + "post": { + "operationId": "confirmQuickLink", "tags": [ "QuickLink" ], "security": [], - "summary": "Customize a simulated QuickLink account", + "summary": "Confirm a linked QuickLink", + "description": "Records consent, applies the chat-history choice, assigns Customer ownership when applicable, and returns the redirect target.", "parameters": [ { "schema": { @@ -48837,68 +64838,49 @@ } ], "requestBody": { - "required": true, "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "profile": { - "type": "object", - "properties": { - "name": { - "type": "string", - "minLength": 1, - "maxLength": 128 - }, - "status": { - "type": "string", - "maxLength": 139 - } - }, - "additionalProperties": false - }, - "accountType": { - "type": "string", - "enum": [ - "personal", - "business" - ] - }, - "replyBehavior": { - "type": "string", - "enum": [ - "off", - "echo" - ] - }, - "failureScenario": { - "type": "string", - "enum": [ - "none", - "reject-send" - ] - }, - "historyFixtureId": { - "type": "string", - "format": "uuid" - } - }, - "additionalProperties": false + "$ref": "#/components/schemas/QuickLinkConfirmRequest" } } } }, "responses": { "200": { - "description": "Test configuration updated", + "description": "Connected", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/QuickLinkState" + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "connected" + ] + }, + "phone": { + "type": "string", + "nullable": true + }, + "historySync": { + "type": "boolean" + }, + "redirectUrl": { + "type": "string", + "nullable": true + } + }, + "required": [ + "status", + "phone", + "historySync", + "redirectUrl" + ] } }, "required": [ @@ -48991,14 +64973,14 @@ } } }, - "/quicklink/{token}/testing/start": { + "/quicklink/{token}/cancel": { "post": { - "operationId": "startQuickLinkTesting", + "operationId": "cancelQuickLinkByToken", "tags": [ "QuickLink" ], "security": [], - "summary": "Start the simulated account reserved by a QuickLink", + "summary": "Cancel a QuickLink from the hosted page", "parameters": [ { "schema": { @@ -49012,14 +64994,64 @@ ], "responses": { "200": { - "description": "Current test connection state", + "description": "Cancelled", "content": { "application/json": { "schema": { "type": "object", "properties": { "data": { - "$ref": "#/components/schemas/QuickLinkState" + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "cancelled" + ] + }, + "redirectUrl": { + "type": "string", + "nullable": true + } + }, + "required": [ + "status", + "redirectUrl" + ] + } + }, + "required": [ + "data" + ] + } + } + } + }, + "202": { + "description": "QuickLink invalidated; Number cleanup pending", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "status": { + "type": "string", + "enum": [ + "cancelled" + ] + }, + "redirectUrl": { + "type": "string", + "nullable": true + } + }, + "required": [ + "status", + "redirectUrl" + ] } }, "required": [ @@ -49112,775 +65144,1404 @@ } } }, - "/quicklink/{token}": { - "get": { - "operationId": "getQuickLinkState", + "/messaging/cloud-api/embedded-signup": { + "post": { + "operationId": "embeddedSignup", "tags": [ - "QuickLink" + "Cloud API" ], - "security": [], - "summary": "Read QuickLink state", - "description": "Returns the hosted page state for a QuickLink. Public; the token in the URL is the credential. Starts linked-device pairing after an allowed connection type has been selected.", - "parameters": [ + "summary": "Advance Meta Cloud API setup for an issued QuickLink", + "security": [ { - "schema": { - "type": "string", - "description": "Opaque QuickLink token from the hosted URL." - }, - "required": true, - "name": "token", - "in": "path" + "BearerAuth": [] } ], + "x-required-scope": "quicklink:manage", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/EmbeddedSignupRequest" + } + } + } + }, "responses": { "200": { - "description": "Current QuickLink state", + "description": "Onboarding progress", "content": { "application/json": { "schema": { "type": "object", "properties": { + "success": { + "type": "boolean", + "enum": [ + true + ] + }, "data": { - "$ref": "#/components/schemas/QuickLinkState" + "type": "object", + "properties": { + "stage": { + "type": "string" + } + }, + "required": [ + "stage" + ] } }, "required": [ + "success", "data" ] } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { - "description": "QuickLink request failed", + "description": "Bad request — request body or params failed validation", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "invalid_parameter", + "message": "Correct the invalid request field.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } - } - }, - "403": { - "description": "QuickLink request failed", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" - } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } }, - "404": { - "description": "QuickLink request failed", + "401": { + "description": "Missing or invalid authentication token", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "authentication_error", + "code": "invalid_credential", + "message": "Supply a valid credential.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, - "409": { - "description": "QuickLink request failed", + "403": { + "description": "Authenticated but not permitted (missing scope or rule violation)", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "permission_error", + "code": "permission_denied", + "message": "The credential does not have access to this resource.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, - "422": { - "description": "QuickLink request failed", + "404": { + "description": "Resource not found", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "resource_not_found", + "message": "The resource is unavailable to this caller.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, - "429": { - "description": "QuickLink request failed", + "410": { + "description": "Direct session signup was retired; create a QuickLink first", + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "resource_gone", + "message": "The requested retained resource has expired or was removed.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#resource-gone" } } } }, "500": { - "description": "QuickLink request failed", + "description": "Internal server error", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "internal_error", + "message": "An unexpected service failure occurred. Keep the request ID for support.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } - } - }, - "503": { - "description": "QuickLink request failed", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" - } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } } } - } + }, + "x-mint": { + "content": "quicklink:manage" + }, + "description": "**Required scope:** `quicklink:manage`" } }, - "/quicklink/{token}/logo": { - "get": { - "operationId": "getQuickLinkLogo", + "/messaging/testing/{projectId}/history-fixtures": { + "post": { + "operationId": "createTestingHistoryFixture", "tags": [ - "QuickLink" + "Testing" ], - "security": [], - "summary": "Read the QuickLink page logo", - "description": "Returns the saved logo image for the page. Public; the token in the URL is the credential.", + "security": [ + { + "BearerAuth": [] + } + ], + "x-required-scope": "sandbox:write", "parameters": [ { "schema": { "type": "string", - "description": "Opaque QuickLink token from the hosted URL." + "format": "uuid" }, "required": true, - "name": "token", + "name": "projectId", "in": "path" } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "messages": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "minLength": 1, + "maxLength": 128 + }, + "senderPhone": { + "type": "string", + "pattern": "^\\+[1-9][0-9]{1,14}$" + }, + "text": { + "type": "string", + "maxLength": 4096 + }, + "timestamp": { + "type": "integer", + "minimum": 0, + "exclusiveMinimum": true + }, + "fromMe": { + "type": "boolean" + } + }, + "required": [ + "id", + "senderPhone", + "text", + "timestamp", + "fromMe" + ], + "additionalProperties": false + }, + "maxItems": 100 + } + }, + "required": [ + "messages" + ], + "additionalProperties": false + } + } + } + }, "responses": { - "200": { - "description": "Logo image", + "201": { + "description": "Project-scoped history fixture", "content": { - "image/png": { + "application/json": { "schema": { - "type": "string", - "format": "binary" + "type": "object", + "properties": { + "fixtureId": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "fixtureId" + ] } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { - "description": "QuickLink request failed", + "description": "Invalid fixture", + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "invalid_parameter", + "message": "Correct the invalid request field.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } } }, "403": { - "description": "QuickLink request failed", + "description": "Test access required", + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "permission_error", + "code": "permission_denied", + "message": "The credential does not have access to this resource.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } } }, "404": { - "description": "QuickLink request failed", + "description": "Project not found", + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "resource_not_found", + "message": "The resource is unavailable to this caller.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } } }, - "409": { - "description": "QuickLink request failed", + "502": { + "description": "Test runtime unavailable", + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "upstream_error", + "code": "upstream_failure", + "message": "The messaging provider could not complete the request.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#upstream-failure" } } } - }, - "422": { - "description": "QuickLink request failed", + } + }, + "x-mint": { + "content": "sandbox:write" + }, + "description": "**Required scope:** `sandbox:write`" + } + }, + "/messaging/testing/{projectId}/events/fixtures": { + "get": { + "operationId": "listTestEventFixtures", + "tags": [ + "Testing" + ], + "security": [ + { + "BearerAuth": [] + } + ], + "summary": "List test event fixtures", + "x-required-scope": "sandbox:read", + "parameters": [ + { + "schema": { + "type": "string", + "format": "uuid" + }, + "required": true, + "name": "projectId", + "in": "path" + } + ], + "responses": { + "200": { + "description": "Available fixtures and the overrides each accepts", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/TestEventFixtureList" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, - "429": { - "description": "QuickLink request failed", + "403": { + "description": "Test numbers access required, or a client token was used", + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "permission_error", + "code": "permission_denied", + "message": "The credential does not have access to this resource.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } } }, - "500": { - "description": "QuickLink request failed", + "404": { + "description": "Project not found", + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "resource_not_found", + "message": "The resource is unavailable to this caller.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } } }, - "503": { - "description": "QuickLink request failed", + "502": { + "description": "Test access could not be verified", + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "upstream_error", + "code": "upstream_failure", + "message": "The messaging provider could not complete the request.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#upstream-failure" } } } } - } + }, + "x-mint": { + "content": "sandbox:read" + }, + "description": "**Required scope:** `sandbox:read`" } }, - "/quicklink/{token}/code": { + "/messaging/testing/{projectId}/events": { "post": { - "operationId": "requestQuickLinkCode", + "operationId": "triggerTestEvent", "tags": [ - "QuickLink" + "Testing" ], - "security": [], - "summary": "Request a QuickLink pairing code", + "security": [ + { + "BearerAuth": [] + } + ], + "summary": "Trigger a test event for a Test number", + "x-required-scope": "sandbox:write", "parameters": [ { "schema": { "type": "string", - "description": "Opaque QuickLink token from the hosted URL." + "format": "uuid" }, "required": true, - "name": "token", + "name": "projectId", "in": "path" + }, + { + "schema": { + "type": "string", + "pattern": "^[\\x21-\\x7e]{1,255}$", + "description": "Optional. Repeating a request with the same key and body reuses the same event ID, so a retry after a failed or uncertain response never creates a second event or duplicate webhook deliveries." + }, + "required": false, + "name": "idempotency-key", + "in": "header" } ], "requestBody": { + "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/QuickLinkCodeRequest" + "$ref": "#/components/schemas/TriggerTestEventRequest" } } } }, "responses": { - "200": { - "description": "Pairing code issued", + "202": { + "description": "Test event accepted for delivery", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "data": { - "type": "object", - "properties": { - "code": { - "type": "string" - }, - "phone": { - "type": "string" - } - }, - "required": [ - "code", - "phone" - ] - } - }, - "required": [ - "data" - ] + "$ref": "#/components/schemas/TriggerTestEventResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { - "description": "QuickLink request failed", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" - } + "description": "Invalid fixture, override, or real session", + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } - } - }, - "402": { - "description": "The team cannot fund pairing", + }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "invalid_parameter", + "message": "Correct the invalid request field.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } } }, "403": { - "description": "QuickLink request failed", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" - } + "description": "Test numbers access required, or a client token was used", + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } - } - }, - "404": { - "description": "QuickLink request failed", + }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "permission_error", + "code": "permission_denied", + "message": "The credential does not have access to this resource.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } } }, - "409": { - "description": "QuickLink request failed", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" - } + "404": { + "description": "Project or Test number not found", + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } - } - }, - "422": { - "description": "QuickLink request failed", + }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "resource_not_found", + "message": "The resource is unavailable to this caller.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } } }, "429": { - "description": "QuickLink request failed", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" - } + "description": "Test event rate limit reached", + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } - } - }, - "500": { - "description": "QuickLink request failed", + }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "rate_limit_error", + "code": "rate_limit_exceeded", + "message": "The request limit was reached. Follow Retry-After when supplied.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#rate-limit-exceeded" } } } }, - "503": { - "description": "QuickLink request failed", + "502": { + "description": "Test event delivery unavailable. Retry with the same Idempotency-Key to avoid a second event.", + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } + }, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "upstream_error", + "code": "upstream_failure", + "message": "The messaging provider could not complete the request.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#upstream-failure" } } } } - } + }, + "x-mint": { + "content": "sandbox:write" + }, + "description": "**Required scope:** `sandbox:write`" } }, - "/quicklink/{token}/confirm": { + "/messaging/voip/calls/{id}/participants/ring": { "post": { - "operationId": "confirmQuickLink", + "operationId": "voipRingParticipant", "tags": [ - "QuickLink" + "VoIP" ], - "security": [], - "summary": "Confirm a linked QuickLink", - "description": "Records consent, applies the chat-history choice, assigns Customer ownership when applicable, and returns the redirect target.", + "summary": "Ring an existing call participant again", + "description": "**Required scope:** `sessions:manage`\n\nRings one non-connected participant already in the upstream call roster. Does not add a participant or change the roster optimistically. Requires the same destination permission as an invitation. Cloud API calls do not support this operation.", + "security": [ + { + "BearerAuth": [] + } + ], + "x-required-scope": "sessions:manage", "parameters": [ { "schema": { "type": "string", - "description": "Opaque QuickLink token from the hosted URL." + "minLength": 1, + "example": "call-123" }, "required": true, - "name": "token", + "name": "id", "in": "path" } ], "requestBody": { + "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/QuickLinkConfirmRequest" + "$ref": "#/components/schemas/VoipAddParticipantRequest" } } } }, "responses": { - "200": { - "description": "Connected", + "202": { + "description": "Ring request accepted", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "data": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "connected" - ] - }, - "phone": { - "type": "string", - "nullable": true - }, - "historySync": { - "type": "boolean" - }, - "redirectUrl": { - "type": "string", - "nullable": true - } - }, - "required": [ - "status", - "phone", - "historySync", - "redirectUrl" - ] - } - }, - "required": [ - "data" - ] + "$ref": "#/components/schemas/SuccessResponse" } } } }, "400": { - "description": "QuickLink request failed", + "description": "Bad request — request body or params failed validation", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "invalid_parameter", + "message": "Correct the invalid request field.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" + } + } + } + }, + "401": { + "description": "Missing or invalid authentication token", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "authentication_error", + "code": "invalid_credential", + "message": "Supply a valid credential.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } } }, "403": { - "description": "QuickLink request failed", + "description": "Authenticated but not permitted (missing scope or rule violation)", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "permission_error", + "code": "permission_denied", + "message": "The credential does not have access to this resource.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } } }, "404": { - "description": "QuickLink request failed", + "description": "Resource not found", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "resource_not_found", + "message": "The resource is unavailable to this caller.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } } }, "409": { - "description": "QuickLink request failed", + "description": "The call is not ready (`state_conflict`), or the calling backend does not support this action (`unsupported_for_connection`).", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "conflict_error", + "code": "state_conflict", + "message": "The request conflicts with the resource state.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#state-conflict" } } } }, - "422": { - "description": "QuickLink request failed", + "410": { + "description": "Call ended — the pod that owned its media is gone (pod_lost)", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "resource_gone", + "message": "The requested retained resource has expired or was removed.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#resource-gone" } } } }, "429": { - "description": "QuickLink request failed", + "description": "Call setup rate exceeded for this client token, or its concurrency limit is reached", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "rate_limit_error", + "code": "rate_limit_exceeded", + "message": "The request limit was reached. Follow Retry-After when supplied.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#rate-limit-exceeded" } } } }, "500": { - "description": "QuickLink request failed", + "description": "Internal server error", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "internal_error", + "message": "An unexpected service failure occurred. Keep the request ID for support.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } } }, "503": { - "description": "QuickLink request failed", + "description": "Call unavailable — the owning pod is at capacity or draining, or call authority cannot be verified", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "service_unavailable", + "message": "A required service is unavailable. Check completion before repeating a write.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" } } } } + }, + "x-mint": { + "content": "sessions:manage" } } }, - "/quicklink/{token}/cancel": { + "/messaging/voip/calls/{id}/reaction": { "post": { - "operationId": "cancelQuickLinkByToken", + "operationId": "sendCallReaction", "tags": [ - "QuickLink" + "VoIP" ], - "security": [], - "summary": "Cancel a QuickLink from the hosted page", + "summary": "Send or clear a call reaction", + "description": "Requires an attached connection owned by the acting participant, sessions:manage, and voip_signal for client tokens. Linked-device calls only; unsupported backends return 409 unsupported_for_connection. Reactions are transient and are not replayed. Hand state is shared by the Number and restored on media reconnection. Do not automatically retry an uncertain result.", + "security": [ + { + "BearerAuth": [] + } + ], + "x-required-scope": "sessions:manage", "parameters": [ { "schema": { "type": "string", - "description": "Opaque QuickLink token from the hosted URL." + "minLength": 1, + "example": "call-123" }, "required": true, - "name": "token", + "name": "id", "in": "path" } ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/VoipReactionRequest" + } + } + } + }, "responses": { - "200": { - "description": "Cancelled", + "202": { + "description": "Reaction accepted", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "data": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "cancelled" - ] - }, - "redirectUrl": { - "type": "string", - "nullable": true - } - }, - "required": [ - "status", - "redirectUrl" - ] - } - }, - "required": [ - "data" - ] + "$ref": "#/components/schemas/SuccessResponse" } } } }, - "202": { - "description": "QuickLink invalidated; Number cleanup pending", + "400": { + "description": "Bad request — request body or params failed validation", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "data": { - "type": "object", - "properties": { - "status": { - "type": "string", - "enum": [ - "cancelled" - ] - }, - "redirectUrl": { - "type": "string", - "nullable": true - } - }, - "required": [ - "status", - "redirectUrl" - ] - } + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "invalid_parameter", + "message": "Correct the invalid request field.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" }, - "required": [ - "data" - ] + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } } }, - "400": { - "description": "QuickLink request failed", + "401": { + "description": "Missing or invalid authentication token", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "authentication_error", + "code": "invalid_credential", + "message": "Supply a valid credential.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } } }, "403": { - "description": "QuickLink request failed", + "description": "Authenticated but not permitted (missing scope or rule violation)", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "permission_error", + "code": "permission_denied", + "message": "The credential does not have access to this resource.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } } }, "404": { - "description": "QuickLink request failed", + "description": "Resource not found", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "resource_not_found", + "message": "The resource is unavailable to this caller.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } } }, "409": { - "description": "QuickLink request failed", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" - } - } - } - }, - "422": { - "description": "QuickLink request failed", + "description": "The call is not ready (`state_conflict`), or the calling backend does not support this action (`unsupported_for_connection`).", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "conflict_error", + "code": "state_conflict", + "message": "The request conflicts with the resource state.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#state-conflict" } } } }, - "429": { - "description": "QuickLink request failed", + "410": { + "description": "Call ended — the pod that owned its media is gone (pod_lost)", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "invalid_request_error", + "code": "resource_gone", + "message": "The requested retained resource has expired or was removed.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#resource-gone" } } } }, "500": { - "description": "QuickLink request failed", + "description": "Internal server error", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "internal_error", + "message": "An unexpected service failure occurred. Keep the request ID for support.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } } }, "503": { - "description": "QuickLink request failed", + "description": "Call unavailable — the owning pod is at capacity or draining, or call authority cannot be verified", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/PublicQuickLinkError" + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "service_unavailable", + "message": "A required service is unavailable. Check completion before repeating a write.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" } } } } + }, + "x-mint": { + "content": "sessions:manage" } } }, - "/messaging/cloud-api/embedded-signup": { + "/messaging/voip/calls/{id}/hand": { "post": { - "operationId": "embeddedSignup", + "operationId": "setCallHandRaised", "tags": [ - "Cloud API" + "VoIP" ], - "summary": "Advance Meta Cloud API setup for an issued QuickLink", + "summary": "Raise or lower the Number's hand in a call", + "description": "Requires an attached connection owned by the acting participant, sessions:manage, and voip_signal for client tokens. Linked-device calls only; unsupported backends return 409 unsupported_for_connection. Reactions are transient and are not replayed. Hand state is shared by the Number and restored on media reconnection. Do not automatically retry an uncertain result.", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "quicklink:manage", + "x-required-scope": "sessions:manage", + "parameters": [ + { + "schema": { + "type": "string", + "minLength": 1, + "example": "call-123" + }, + "required": true, + "name": "id", + "in": "path" + } + ], "requestBody": { "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/EmbeddedSignupRequest" + "$ref": "#/components/schemas/VoipHandRequest" } } } }, "responses": { - "200": { - "description": "Onboarding progress", + "202": { + "description": "Hand state accepted", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "success": { - "type": "boolean", - "enum": [ - true - ] - }, - "data": { - "type": "object", - "properties": { - "stage": { - "type": "string" - } - }, - "required": [ - "stage" - ] - } - }, - "required": [ - "success", - "data" - ] + "$ref": "#/components/schemas/SuccessResponse" } } } @@ -49969,8 +66630,29 @@ } } }, + "409": { + "description": "The call is not ready (`state_conflict`), or the calling backend does not support this action (`unsupported_for_connection`).", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "conflict_error", + "code": "state_conflict", + "message": "The request conflicts with the resource state.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#state-conflict" + } + } + } + }, "410": { - "description": "Direct session signup was retired; create a QuickLink first", + "description": "Call ended — the pod that owned its media is gone (pod_lost)", "content": { "application/json": { "schema": { @@ -50010,113 +66692,71 @@ } } } + }, + "503": { + "description": "Call unavailable — the owning pod is at capacity or draining, or call authority cannot be verified", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "service_unavailable", + "message": "A required service is unavailable. Check completion before repeating a write.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" + } + } + } } }, "x-mint": { - "content": "quicklink:manage" - }, - "description": "**Required scope:** `quicklink:manage`" + "content": "sessions:manage" + } } }, - "/messaging/testing/{projectId}/history-fixtures": { + "/messaging/voip/call-links": { "post": { - "operationId": "createTestingHistoryFixture", + "operationId": "voipCreateCallLink", "tags": [ - "Testing" + "VoIP" ], + "summary": "Create a WhatsApp call link", + "description": "**Required scope:** `sessions:manage`\n\nRequires a server credential with sessions:manage and a Linked Device Number with calling enabled. Client tokens and Cloud API-only Numbers are refused. This operation does not place or join a call. Idempotency-Key is not supported; do not automatically retry an unknown outcome. Keep call-link tokens and URLs private.", "security": [ { "BearerAuth": [] } ], - "x-required-scope": "sandbox:write", - "parameters": [ - { - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true, - "name": "projectId", - "in": "path" - } - ], + "x-required-scope": "sessions:manage", "requestBody": { "required": true, "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "messages": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "minLength": 1, - "maxLength": 128 - }, - "senderPhone": { - "type": "string", - "pattern": "^\\+[1-9][0-9]{1,14}$" - }, - "text": { - "type": "string", - "maxLength": 4096 - }, - "timestamp": { - "type": "integer", - "minimum": 0, - "exclusiveMinimum": true - }, - "fromMe": { - "type": "boolean" - } - }, - "required": [ - "id", - "senderPhone", - "text", - "timestamp", - "fromMe" - ], - "additionalProperties": false - }, - "maxItems": 100 - } - }, - "required": [ - "messages" - ], - "additionalProperties": false + "$ref": "#/components/schemas/VoipCreateCallLinkRequest" } } } }, "responses": { "201": { - "description": "Project-scoped history fixture", + "description": "Link created", "content": { "application/json": { "schema": { - "type": "object", - "properties": { - "fixtureId": { - "type": "string", - "format": "uuid" - } - }, - "required": [ - "fixtureId" - ] + "$ref": "#/components/schemas/VoipCreatedCallLinkEnvelope" } } } }, "400": { - "description": "Invalid fixture", + "description": "Bad request — request body or params failed validation", "content": { "application/json": { "schema": { @@ -50136,8 +66776,29 @@ } } }, + "401": { + "description": "Missing or invalid authentication token", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "authentication_error", + "code": "invalid_credential", + "message": "Supply a valid credential.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" + } + } + } + }, "403": { - "description": "Test access required", + "description": "Authenticated but not permitted (missing scope or rule violation)", "content": { "application/json": { "schema": { @@ -50158,7 +66819,7 @@ } }, "404": { - "description": "Project not found", + "description": "Resource not found", "content": { "application/json": { "schema": { @@ -50178,8 +66839,8 @@ } } }, - "502": { - "description": "Test runtime unavailable", + "409": { + "description": "Unsupported connection or another call-link request is in progress", "content": { "application/json": { "schema": { @@ -50187,62 +66848,20 @@ }, "example": { "error": { - "type": "upstream_error", - "code": "upstream_failure", - "message": "The messaging provider could not complete the request.", + "type": "conflict_error", + "code": "state_conflict", + "message": "The request conflicts with the resource state.", "param": null, "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" }, "data": null, - "docs": "https://docs.polymorfa.com/api/errors#upstream-failure" - } - } - } - } - }, - "x-mint": { - "content": "sandbox:write" - }, - "description": "**Required scope:** `sandbox:write`" - } - }, - "/messaging/testing/{projectId}/events/fixtures": { - "get": { - "operationId": "listTestEventFixtures", - "tags": [ - "Testing" - ], - "security": [ - { - "BearerAuth": [] - } - ], - "summary": "List test event fixtures", - "x-required-scope": "sandbox:read", - "parameters": [ - { - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true, - "name": "projectId", - "in": "path" - } - ], - "responses": { - "200": { - "description": "Available fixtures and the overrides each accepts", - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/TestEventFixtureList" + "docs": "https://docs.polymorfa.com/api/errors#state-conflict" } } } }, - "403": { - "description": "Test numbers access required, or a client token was used", + "500": { + "description": "Internal server error", "content": { "application/json": { "schema": { @@ -50250,20 +66869,20 @@ }, "example": { "error": { - "type": "permission_error", - "code": "permission_denied", - "message": "The credential does not have access to this resource.", + "type": "api_error", + "code": "internal_error", + "message": "An unexpected service failure occurred. Keep the request ID for support.", "param": null, "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" }, "data": null, - "docs": "https://docs.polymorfa.com/api/errors#permission-denied" + "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } } }, - "404": { - "description": "Project not found", + "502": { + "description": "WhatsApp failed or returned an invalid response; creation outcome can be unknown", "content": { "application/json": { "schema": { @@ -50271,20 +66890,20 @@ }, "example": { "error": { - "type": "invalid_request_error", - "code": "resource_not_found", - "message": "The resource is unavailable to this caller.", + "type": "upstream_error", + "code": "upstream_failure", + "message": "The messaging provider could not complete the request.", "param": null, "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" }, "data": null, - "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" + "docs": "https://docs.polymorfa.com/api/errors#upstream-failure" } } } }, - "502": { - "description": "Test access could not be verified", + "503": { + "description": "Calling settings, Number authority or the connection are unavailable", "content": { "application/json": { "schema": { @@ -50292,82 +66911,61 @@ }, "example": { "error": { - "type": "upstream_error", - "code": "upstream_failure", - "message": "The messaging provider could not complete the request.", + "type": "api_error", + "code": "service_unavailable", + "message": "A required service is unavailable. Check completion before repeating a write.", "param": null, "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" }, "data": null, - "docs": "https://docs.polymorfa.com/api/errors#upstream-failure" + "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" } } } } }, "x-mint": { - "content": "sandbox:read" - }, - "description": "**Required scope:** `sandbox:read`" + "content": "sessions:manage" + } } }, - "/messaging/testing/{projectId}/events": { + "/messaging/voip/call-links/preview": { "post": { - "operationId": "triggerTestEvent", + "operationId": "voipPreviewCallLink", "tags": [ - "Testing" + "VoIP" ], + "summary": "Preview a WhatsApp call link", + "description": "**Required scope:** `sessions:manage`\n\nRequires a server credential with sessions:manage and a Linked Device Number with calling enabled. Client tokens and Cloud API-only Numbers are refused. This operation does not place or join a call. Idempotency-Key is not supported; do not automatically retry an unknown outcome. Keep call-link tokens and URLs private.", "security": [ { "BearerAuth": [] } ], - "summary": "Trigger a test event for a Test number", - "x-required-scope": "sandbox:write", - "parameters": [ - { - "schema": { - "type": "string", - "format": "uuid" - }, - "required": true, - "name": "projectId", - "in": "path" - }, - { - "schema": { - "type": "string", - "pattern": "^[\\x21-\\x7e]{1,255}$", - "description": "Optional. Repeating a request with the same key and body reuses the same event ID, so a retry after a failed or uncertain response never creates a second event or duplicate webhook deliveries." - }, - "required": false, - "name": "idempotency-key", - "in": "header" - } - ], + "x-required-scope": "sessions:manage", "requestBody": { "required": true, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TriggerTestEventRequest" + "$ref": "#/components/schemas/VoipPreviewCallLinkRequest" } } } }, "responses": { - "202": { - "description": "Test event accepted for delivery", + "200": { + "description": "Link preview", "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/TriggerTestEventResponse" + "$ref": "#/components/schemas/VoipPreviewedCallLinkEnvelope" } } } }, "400": { - "description": "Invalid fixture, override, or real session", + "description": "Bad request — request body or params failed validation", "content": { "application/json": { "schema": { @@ -50387,8 +66985,29 @@ } } }, + "401": { + "description": "Missing or invalid authentication token", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "authentication_error", + "code": "invalid_credential", + "message": "Supply a valid credential.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" + } + } + } + }, "403": { - "description": "Test numbers access required, or a client token was used", + "description": "Authenticated but not permitted (missing scope or rule violation)", "content": { "application/json": { "schema": { @@ -50409,7 +67028,7 @@ } }, "404": { - "description": "Project or Test number not found", + "description": "Resource not found", "content": { "application/json": { "schema": { @@ -50429,8 +67048,8 @@ } } }, - "429": { - "description": "Test event rate limit reached", + "409": { + "description": "Unsupported connection or another call-link request is in progress", "content": { "application/json": { "schema": { @@ -50438,20 +67057,41 @@ }, "example": { "error": { - "type": "rate_limit_error", - "code": "rate_limit_exceeded", - "message": "The request limit was reached. Follow Retry-After when supplied.", + "type": "conflict_error", + "code": "state_conflict", + "message": "The request conflicts with the resource state.", "param": null, "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" }, "data": null, - "docs": "https://docs.polymorfa.com/api/errors#rate-limit-exceeded" + "docs": "https://docs.polymorfa.com/api/errors#state-conflict" + } + } + } + }, + "500": { + "description": "Internal server error", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "internal_error", + "message": "An unexpected service failure occurred. Keep the request ID for support.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } } }, "502": { - "description": "Test event delivery unavailable. Retry with the same Idempotency-Key to avoid a second event.", + "description": "WhatsApp failed or returned an invalid response; creation outcome can be unknown", "content": { "application/json": { "schema": { @@ -50470,12 +67110,32 @@ } } } + }, + "503": { + "description": "Calling settings, Number authority or the connection are unavailable", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PublicError" + }, + "example": { + "error": { + "type": "api_error", + "code": "service_unavailable", + "message": "A required service is unavailable. Check completion before repeating a write.", + "param": null, + "request_id": "5f0c2a8e-3b1d-4c6f-9e2a-7d4b1c8f6a30" + }, + "data": null, + "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" + } + } + } } }, "x-mint": { - "content": "sandbox:write" - }, - "description": "**Required scope:** `sandbox:write`" + "content": "sessions:manage" + } } } }, diff --git a/contracts/openapi.platform.json b/contracts/openapi.platform.json index 399f8488..feaad173 100644 --- a/contracts/openapi.platform.json +++ b/contracts/openapi.platform.json @@ -3,7 +3,7 @@ "info": { "title": "Polymorfa Console & Management API", "version": "1.0.0", - "description": "Complete console/management surface. Resource-oriented REST: `{param}` path variables, single-word action sub-resources.\n\nNamespaces decide inclusion:\n- `/platform/…` — **public registry** (219 endpoints): each endpoint declares either a scoped `pmfa_` organization key or a verified live dashboard JWT, or dashboard-only authorization when a specific user's authority is required. Only implemented handlers are published to customers and SDKs.\n- `/console/…` — **console-only** (99 endpoints): verified dashboard identity. EXCLUDED from the published spec; reason per endpoint.\n\nThe published customer spec = this document filtered to `x-audience: public` endpoints with an implemented runtime handler.\nMost transitional body/response schemas remain loose (`additionalProperties: true`); session-control operations use explicit strict schemas." + "description": "Complete console/management surface. Resource-oriented REST: `{param}` path variables, single-word action sub-resources.\n\nNamespaces decide inclusion:\n- `/platform/…` — **public registry** (220 endpoints): each endpoint declares either a scoped `pmfa_` organization key or a verified live dashboard JWT, or dashboard-only authorization when a specific user's authority is required. Only implemented handlers are published to customers and SDKs.\n- `/console/…` — **console-only** (99 endpoints): verified dashboard identity. EXCLUDED from the published spec; reason per endpoint.\n\nThe published customer spec = this document filtered to `x-audience: public` endpoints with an implemented runtime handler.\nMost transitional body/response schemas remain loose (`additionalProperties: true`); session-control operations use explicit strict schemas." }, "servers": [ { @@ -15,6 +15,52 @@ } ], "paths": { + "/api/account/cloud-health": { + "get": { + "operationId": "getAccountCloudHealth", + "summary": "Read a Number's redacted Meta account health", + "tags": ["Account"], + "x-audience": "console", + "x-dashboard-only": true, + "x-excluded-reason": "Dashboard identity and live organization membership are required; the account projection is not an API-key operation.", + "security": [{"ConsoleSession": []}], + "parameters": [ + {"name": "projectId", "in": "query", "required": true, "schema": {"type": "string", "format": "uuid"}}, + {"name": "session", "in": "query", "required": true, "schema": {"type": "string"}} + ], + "responses": { + "200": {"description": "Redacted account, connection, webhook and messaging evidence with a scan ID and observation time", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AccountCloudHealthResponse"}}}}, + "400": {"description": "Invalid project or Number"}, + "401": {"description": "Missing or invalid dashboard JWT, or identity no longer active", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/DashboardUnauthorizedError"}}}}, + "403": {"description": "Cloud onboarding is unavailable"}, + "404": {"description": "Active Cloud connection or health projection not found"}, + "503": {"description": "Health projection unavailable"} + } + } + }, + "/api/account/cloud-health/export": { + "get": { + "operationId": "exportAccountCloudHealth", + "summary": "Download a Number's redacted Meta account health", + "tags": ["Account"], + "x-audience": "console", + "x-dashboard-only": true, + "x-excluded-reason": "Dashboard identity and live organization membership are required; diagnostics are not an API-key operation.", + "security": [{"ConsoleSession": []}], + "parameters": [ + {"name": "projectId", "in": "query", "required": true, "schema": {"type": "string", "format": "uuid"}}, + {"name": "session", "in": "query", "required": true, "schema": {"type": "string"}} + ], + "responses": { + "200": {"description": "JSON attachment with the same redacted projection as the account read", "headers": {"Content-Disposition": {"description": "Attachment filename for the redacted JSON export", "schema": {"type": "string"}}}, "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AccountCloudHealthResponse"}}}}, + "400": {"description": "Invalid project or Number"}, + "401": {"description": "Missing or invalid dashboard JWT, or identity no longer active", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/DashboardUnauthorizedError"}}}}, + "403": {"description": "Cloud onboarding is unavailable"}, + "404": {"description": "Active Cloud connection or health projection not found"}, + "503": {"description": "Health projection unavailable"} + } + } + }, "/api/account/meta-app": { "get": { "operationId": "getTeamMetaApp", @@ -366,6 +412,15 @@ "type": "string", "example": "no-cache, no-transform" } + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } }, "content": { @@ -384,6 +439,17 @@ "$ref": "#/components/schemas/PlatformAccessPublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -394,6 +460,17 @@ "$ref": "#/components/schemas/PlatformAccessPublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -404,6 +481,17 @@ "$ref": "#/components/schemas/PlatformAccessPublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "406": { @@ -414,6 +502,17 @@ "$ref": "#/components/schemas/PlatformAccessPublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "410": { @@ -424,6 +523,17 @@ "$ref": "#/components/schemas/PlatformAccessPublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "429": { @@ -434,6 +544,17 @@ "$ref": "#/components/schemas/PlatformAccessPublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { @@ -444,6 +565,17 @@ "$ref": "#/components/schemas/PlatformAccessPublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -506,6 +638,17 @@ "$ref": "#/components/schemas/PlatformAccessEventStreamAckResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -516,6 +659,17 @@ "$ref": "#/components/schemas/PlatformAccessPublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -526,6 +680,17 @@ "$ref": "#/components/schemas/PlatformAccessPublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -536,6 +701,17 @@ "$ref": "#/components/schemas/PlatformAccessPublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -546,6 +722,17 @@ "$ref": "#/components/schemas/PlatformAccessPublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { @@ -556,6 +743,17 @@ "$ref": "#/components/schemas/PlatformAccessPublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "429": { @@ -566,6 +764,17 @@ "$ref": "#/components/schemas/PlatformAccessPublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { @@ -576,6 +785,17 @@ "$ref": "#/components/schemas/PlatformAccessPublicError" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -624,6 +844,17 @@ "$ref": "#/components/schemas/PlatformAccessMintClientTokenResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -645,6 +876,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -666,6 +908,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -687,6 +940,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -708,6 +972,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -729,6 +1004,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -771,6 +1057,17 @@ "$ref": "#/components/schemas/PlatformAccessGetClientRulesResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -792,6 +1089,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -813,6 +1121,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -834,6 +1153,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -855,6 +1185,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -876,6 +1217,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -933,6 +1285,17 @@ "$ref": "#/components/schemas/PlatformAccessSuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -954,6 +1317,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -975,6 +1349,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -996,6 +1381,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -1017,6 +1413,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -1038,6 +1445,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -1078,6 +1496,17 @@ "$ref": "#/components/schemas/PlatformAccessSuccessResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -1099,6 +1528,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -1120,6 +1560,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -1141,6 +1592,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -1162,6 +1624,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -1183,6 +1656,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -1227,6 +1711,17 @@ "$ref": "#/components/schemas/PlatformAccessGetCallSettingsResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -1248,6 +1743,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -1269,6 +1775,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -1290,6 +1807,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -1311,6 +1839,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -1332,6 +1871,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -1385,6 +1935,17 @@ "$ref": "#/components/schemas/PlatformAccessUpdateCallSettingsResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -1406,6 +1967,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -1427,6 +1999,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -1448,6 +2031,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -1469,6 +2063,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { @@ -1490,6 +2095,17 @@ "docs": "https://docs.polymorfa.com/api/errors#state-conflict" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -1511,6 +2127,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -1543,6 +2170,17 @@ "$ref": "#/components/schemas/PlatformAccessGetCallRetentionResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -1564,6 +2202,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -1585,6 +2234,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -1606,6 +2266,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -1627,6 +2298,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -1648,6 +2330,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -1688,6 +2381,17 @@ "$ref": "#/components/schemas/PlatformAccessUpdateCallRetentionResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -1709,6 +2413,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -1730,6 +2445,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -1751,6 +2477,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -1772,6 +2509,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { @@ -1793,6 +2541,17 @@ "docs": "https://docs.polymorfa.com/api/errors#state-conflict" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -1814,6 +2573,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -1976,6 +2746,15 @@ }, "required": false, "description": "Cursor for the next page. Absent on the last page." + }, + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" } }, "content": { @@ -2012,6 +2791,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -2033,6 +2823,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -2054,6 +2855,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -2075,6 +2887,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -2096,6 +2919,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -2128,6 +2962,17 @@ "$ref": "#/components/schemas/PlatformAccessGetSipEndpointResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -2149,6 +2994,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -2170,6 +3026,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -2191,6 +3058,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -2212,6 +3090,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -2233,6 +3122,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -2277,6 +3177,17 @@ "$ref": "#/components/schemas/PlatformAccessListSipTrunksResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -2298,6 +3209,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -2319,6 +3241,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -2340,6 +3273,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -2361,6 +3305,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -2382,6 +3337,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -2422,6 +3388,17 @@ "$ref": "#/components/schemas/PlatformAccessCreateSipTrunkResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -2443,6 +3420,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -2464,6 +3452,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -2485,6 +3484,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -2506,6 +3516,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { @@ -2527,6 +3548,17 @@ "docs": "https://docs.polymorfa.com/api/errors#sip-trunk-limit" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -2548,6 +3580,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { @@ -2569,6 +3612,17 @@ "docs": "https://docs.polymorfa.com/api/errors#sip-unavailable" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -2611,6 +3665,17 @@ "$ref": "#/components/schemas/PlatformAccessGetSipTrunkResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -2632,6 +3697,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -2653,6 +3729,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -2674,6 +3761,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -2695,6 +3793,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -2716,6 +3825,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -2768,6 +3888,17 @@ "$ref": "#/components/schemas/PlatformAccessUpdateSipTrunkResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -2789,6 +3920,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -2810,6 +3952,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -2831,6 +3984,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -2852,6 +4016,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { @@ -2873,6 +4048,17 @@ "docs": "https://docs.polymorfa.com/api/errors#sip-trunk-revision-conflict" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -2894,6 +4080,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { @@ -2915,6 +4112,17 @@ "docs": "https://docs.polymorfa.com/api/errors#sip-unavailable" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -2956,6 +4164,17 @@ "$ref": "#/components/schemas/PlatformAccessDeleteSipTrunkResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -2977,6 +4196,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -2998,6 +4228,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -3019,6 +4260,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -3040,6 +4292,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { @@ -3061,6 +4324,17 @@ "docs": "https://docs.polymorfa.com/api/errors#sip-trunk-in-use" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -3082,6 +4356,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -3125,6 +4410,17 @@ "$ref": "#/components/schemas/PlatformAccessRotateSipTrunkCredentialsResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -3146,6 +4442,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -3167,6 +4474,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -3188,6 +4506,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -3209,6 +4538,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { @@ -3230,6 +4570,17 @@ "docs": "https://docs.polymorfa.com/api/errors#state-conflict" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -3251,6 +4602,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { @@ -3272,6 +4634,17 @@ "docs": "https://docs.polymorfa.com/api/errors#sip-unavailable" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -3304,6 +4677,17 @@ "$ref": "#/components/schemas/PlatformAccessGetCallPolicyResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -3325,6 +4709,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -3346,6 +4741,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -3367,6 +4773,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -3388,6 +4805,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -3409,6 +4837,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -3449,6 +4888,17 @@ "$ref": "#/components/schemas/PlatformAccessUpdateCallPolicyResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -3470,6 +4920,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -3491,6 +4952,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -3512,6 +4984,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -3533,6 +5016,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { @@ -3554,6 +5048,17 @@ "docs": "https://docs.polymorfa.com/api/errors#state-conflict" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -3575,6 +5080,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -3651,6 +5167,17 @@ "$ref": "#/components/schemas/PlatformAccessListCallOptOutsResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -3672,6 +5199,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -3693,6 +5231,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -3714,6 +5263,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -3735,6 +5295,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -3756,6 +5327,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -3796,6 +5378,17 @@ "$ref": "#/components/schemas/PlatformAccessExistingCallOptOutResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "201": { @@ -3806,6 +5399,17 @@ "$ref": "#/components/schemas/PlatformAccessCreateCallOptOutResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -3827,6 +5431,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -3848,6 +5463,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -3869,6 +5495,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -3890,6 +5527,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { @@ -3911,6 +5559,17 @@ "docs": "https://docs.polymorfa.com/api/errors#call-opt-out-limit" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -3932,6 +5591,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -3974,6 +5644,17 @@ "$ref": "#/components/schemas/PlatformAccessImportCallOptOutsResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -3995,6 +5676,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -4016,6 +5708,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -4037,6 +5740,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -4058,6 +5772,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { @@ -4079,6 +5804,17 @@ "docs": "https://docs.polymorfa.com/api/errors#call-opt-out-limit" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -4100,6 +5836,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -4143,6 +5890,17 @@ "$ref": "#/components/schemas/PlatformAccessDeleteCallOptOutResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -4164,6 +5922,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -4185,6 +5954,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -4206,6 +5986,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -4227,6 +6018,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -4248,6 +6050,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -4321,6 +6134,17 @@ "$ref": "#/components/schemas/PlatformAccessListVoiceAudioResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -4342,6 +6166,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -4363,6 +6198,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -4384,6 +6230,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -4405,6 +6262,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -4426,6 +6294,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -4466,6 +6345,17 @@ "$ref": "#/components/schemas/PlatformAccessCreateVoiceAudioUploadResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -4487,6 +6377,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -4508,6 +6409,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "402": { @@ -4529,6 +6441,17 @@ "docs": "https://docs.polymorfa.com/api/errors#gate-limit-reached" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -4550,6 +6473,17 @@ "docs": "https://docs.polymorfa.com/api/errors#voice-not-enabled" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -4571,6 +6505,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { @@ -4592,6 +6537,17 @@ "docs": "https://docs.polymorfa.com/api/errors#state-conflict" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -4613,6 +6569,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { @@ -4634,6 +6601,17 @@ "docs": "https://docs.polymorfa.com/api/errors#voice-unavailable" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -4676,6 +6654,17 @@ "$ref": "#/components/schemas/PlatformAccessSynthesizeVoiceAudioResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -4697,6 +6686,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -4718,6 +6718,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "402": { @@ -4739,6 +6750,17 @@ "docs": "https://docs.polymorfa.com/api/errors#gate-limit-reached" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -4760,6 +6782,17 @@ "docs": "https://docs.polymorfa.com/api/errors#voice-not-enabled" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -4781,6 +6814,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { @@ -4802,6 +6846,17 @@ "docs": "https://docs.polymorfa.com/api/errors#state-conflict" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "422": { @@ -4823,6 +6878,17 @@ "docs": "https://docs.polymorfa.com/api/errors#provider-credential-invalid" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -4844,6 +6910,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { @@ -4865,6 +6942,17 @@ "docs": "https://docs.polymorfa.com/api/errors#voice-unavailable" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -4908,6 +6996,17 @@ "$ref": "#/components/schemas/PlatformAccessCompleteVoiceAudioUploadResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -4929,6 +7028,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -4950,6 +7060,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -4971,6 +7092,17 @@ "docs": "https://docs.polymorfa.com/api/errors#voice-not-enabled" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -4992,6 +7124,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { @@ -5013,6 +7156,17 @@ "docs": "https://docs.polymorfa.com/api/errors#state-conflict" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -5034,6 +7188,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { @@ -5055,6 +7220,17 @@ "docs": "https://docs.polymorfa.com/api/errors#voice-unavailable" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -5097,6 +7273,17 @@ "$ref": "#/components/schemas/PlatformAccessGetVoiceAudioResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -5118,6 +7305,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -5139,6 +7337,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -5160,6 +7369,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -5181,6 +7401,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -5202,6 +7433,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -5254,6 +7496,17 @@ "$ref": "#/components/schemas/PlatformAccessUpdateVoiceAudioResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -5275,6 +7528,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -5296,6 +7560,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -5317,6 +7592,17 @@ "docs": "https://docs.polymorfa.com/api/errors#voice-not-enabled" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -5338,6 +7624,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { @@ -5359,6 +7656,17 @@ "docs": "https://docs.polymorfa.com/api/errors#voice-asset-revision-conflict" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -5380,6 +7688,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -5421,6 +7740,17 @@ "$ref": "#/components/schemas/PlatformAccessDeleteVoiceAudioResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -5442,6 +7772,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -5463,6 +7804,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -5484,6 +7836,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -5505,6 +7868,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { @@ -5526,6 +7900,17 @@ "docs": "https://docs.polymorfa.com/api/errors#voice-asset-in-use" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -5547,6 +7932,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -5590,6 +7986,17 @@ "$ref": "#/components/schemas/PlatformAccessGetVoiceAudioPreviewResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -5611,6 +8018,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -5632,6 +8050,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -5653,6 +8082,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -5674,6 +8114,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { @@ -5695,6 +8146,17 @@ "docs": "https://docs.polymorfa.com/api/errors#asset-not-ready" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -5716,6 +8178,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { @@ -5737,6 +8210,17 @@ "docs": "https://docs.polymorfa.com/api/errors#voice-unavailable" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -5781,6 +8265,17 @@ "$ref": "#/components/schemas/PlatformAccessListVoiceProviderCredentialsResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -5802,6 +8297,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -5823,6 +8329,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -5844,6 +8361,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -5865,6 +8393,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -5886,6 +8425,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -5926,6 +8476,17 @@ "$ref": "#/components/schemas/PlatformAccessCreateVoiceProviderCredentialResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -5947,6 +8508,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -5968,6 +8540,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "402": { @@ -5989,6 +8572,17 @@ "docs": "https://docs.polymorfa.com/api/errors#gate-limit-reached" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -6010,6 +8604,17 @@ "docs": "https://docs.polymorfa.com/api/errors#voice-not-enabled" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -6031,6 +8636,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { @@ -6052,6 +8668,17 @@ "docs": "https://docs.polymorfa.com/api/errors#state-conflict" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "422": { @@ -6073,6 +8700,17 @@ "docs": "https://docs.polymorfa.com/api/errors#provider-credential-invalid" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -6094,6 +8732,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { @@ -6115,6 +8764,17 @@ "docs": "https://docs.polymorfa.com/api/errors#voice-unavailable" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -6157,6 +8817,17 @@ "$ref": "#/components/schemas/PlatformAccessGetVoiceProviderCredentialResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -6178,6 +8849,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -6199,6 +8881,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -6220,6 +8913,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -6241,6 +8945,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -6262,6 +8977,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -6304,6 +9030,17 @@ "$ref": "#/components/schemas/PlatformAccessDeleteVoiceProviderCredentialResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -6325,6 +9062,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -6346,6 +9094,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -6367,6 +9126,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -6388,6 +9158,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -6409,6 +9190,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -6452,6 +9244,17 @@ "$ref": "#/components/schemas/PlatformAccessVerifyVoiceProviderCredentialResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -6473,6 +9276,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -6494,6 +9308,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -6515,6 +9340,17 @@ "docs": "https://docs.polymorfa.com/api/errors#voice-not-enabled" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -6536,6 +9372,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -6557,6 +9404,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { @@ -6578,6 +9436,17 @@ "docs": "https://docs.polymorfa.com/api/errors#voice-unavailable" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -6619,6 +9488,17 @@ "$ref": "#/components/schemas/PlatformAccessGetSessionResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -6640,6 +9520,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -6661,6 +9552,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -6682,6 +9584,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -6703,6 +9616,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -6724,6 +9648,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -6773,6 +9708,17 @@ "$ref": "#/components/schemas/PlatformAccessUpdateSessionResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -6794,6 +9740,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -6815,6 +9772,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -6836,6 +9804,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -6857,6 +9836,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "409": { @@ -6878,6 +9868,17 @@ "docs": "https://docs.polymorfa.com/api/errors#state-conflict" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -6899,6 +9900,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "503": { @@ -6920,6 +9932,17 @@ "docs": "https://docs.polymorfa.com/api/errors#service-unavailable" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -7131,6 +10154,17 @@ "$ref": "#/components/schemas/PlatformAccessOperationAccepted" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -7152,6 +10186,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -7173,6 +10218,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -7194,6 +10250,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -7215,6 +10282,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -7236,6 +10314,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -7278,6 +10367,17 @@ "$ref": "#/components/schemas/PlatformAccessOperationAccepted" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -7299,6 +10399,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -7320,6 +10431,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -7341,6 +10463,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -7362,6 +10495,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -7383,6 +10527,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -7425,6 +10580,17 @@ "$ref": "#/components/schemas/PlatformAccessGetSessionAccountResponse" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -7446,6 +10612,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -7467,6 +10644,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -7488,6 +10676,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -7509,6 +10708,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -7530,6 +10740,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -7572,6 +10793,17 @@ "$ref": "#/components/schemas/PlatformAccessHandoffKeysSuccess" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -7593,6 +10825,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -7614,6 +10857,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -7635,6 +10889,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -7656,6 +10921,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -7677,6 +10953,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -7728,6 +11015,17 @@ "$ref": "#/components/schemas/PlatformAccessHandoffImportSuccess" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -7749,6 +11047,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -7770,6 +11079,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -7791,6 +11111,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -7812,6 +11143,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -7833,6 +11175,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -7884,6 +11237,17 @@ "$ref": "#/components/schemas/PlatformAccessUploadAppStateKeysSuccess" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -7905,6 +11269,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -7926,6 +11301,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -7947,6 +11333,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -7968,6 +11365,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -7989,6 +11397,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -8040,6 +11459,17 @@ "$ref": "#/components/schemas/PlatformAccessHandoffAckSuccess" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "400": { @@ -8061,6 +11491,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-parameter" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "401": { @@ -8082,6 +11523,17 @@ "docs": "https://docs.polymorfa.com/api/errors#invalid-credential" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "403": { @@ -8103,6 +11555,17 @@ "docs": "https://docs.polymorfa.com/api/errors#permission-denied" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "404": { @@ -8124,6 +11587,17 @@ "docs": "https://docs.polymorfa.com/api/errors#resource-not-found" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } }, "500": { @@ -8145,6 +11619,17 @@ "docs": "https://docs.polymorfa.com/api/errors#internal-error" } } + }, + "headers": { + "X-RateLimit-Limit": { + "$ref": "#/components/headers/X-RateLimit-Limit" + }, + "X-RateLimit-Remaining": { + "$ref": "#/components/headers/X-RateLimit-Remaining" + }, + "X-RateLimit-Reset": { + "$ref": "#/components/headers/X-RateLimit-Reset" + } } } }, @@ -15740,8 +19225,8 @@ "tags": [ "campaigns" ], - "summary": "List platform campaigns", - "description": "Public Management API. Requires `campaigns:read`. Project tokens only access resources in their own project.", + "summary": "List campaigns", + "description": "Lists campaigns in the selected project, including each campaign's stored send window and delivery counts.\n\nRequires `campaigns:read`. Project tokens only access resources in their own project.", "x-audience": "public", "x-polymorfa-resource-boundary": "project", "x-excluded-from-public": false, @@ -15792,19 +19277,34 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DataEnvelope" + "type": "object", + "additionalProperties": false, + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/PlatformCampaign" + } + } + }, + "required": [ + "data" + ] } } } }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, - "501": { - "$ref": "#/components/responses/NotImplemented" + "404": { + "$ref": "#/components/responses/NotFound" } } }, @@ -15861,8 +19361,7 @@ "additionalProperties": false, "properties": { "data": { - "type": "object", - "additionalProperties": true + "$ref": "#/components/schemas/PlatformCampaign" } }, "required": [ @@ -15894,7 +19393,7 @@ "campaigns" ], "summary": "Get campaign", - "description": "Public Management API. Requires `campaigns:read`. Project tokens only access resources in their own project.", + "description": "Returns one campaign and its stored send window, or null when no matching campaign exists in the selected project.\n\nRequires `campaigns:read`. Project tokens only access resources in their own project.", "x-audience": "public", "x-polymorfa-resource-boundary": "project", "x-excluded-from-public": false, @@ -15946,19 +19445,36 @@ "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/DataEnvelope" + "type": "object", + "additionalProperties": false, + "properties": { + "data": { + "nullable": true, + "allOf": [ + { + "$ref": "#/components/schemas/PlatformCampaign" + } + ] + } + }, + "required": [ + "data" + ] } } } }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, - "501": { - "$ref": "#/components/responses/NotImplemented" + "404": { + "$ref": "#/components/responses/NotFound" } } }, @@ -15968,7 +19484,7 @@ "campaigns" ], "summary": "Update campaign", - "description": "Updates a campaign. On an unlaunched draft, a new `senderConfig` passes the same checks as create and plans are checked at launch. Once the campaign has launched, including a scheduled launch that has not started, a new `senderConfig` passes the launch checks and can answer `402 campaigns_not_entitled` or `400 invalid_parameter`. `recipientListId` points the campaign at another audience while it is still an unlaunched draft and replaces its existing recipients; after launch it answers `409`. A sender edit to a draft that launches while the edit is being saved also answers `409`. A changed `scheduledAt` after launch answers `409`; use reschedule to move a scheduled start. A non-null `scheduledAt` must be integer Unix milliseconds from zero through the JavaScript Date maximum.\n\nRequires `campaigns:manage`. Project tokens only access resources in their own project.", + "description": "Updates a campaign. On an unlaunched draft, a new `senderConfig` passes the same checks as create and plans are checked at launch. Once the campaign has launched, including a scheduled launch that has not started, a new `senderConfig` passes the launch checks and can answer `402 campaigns_not_entitled` or `400 invalid_parameter`. `recipientListId` points the campaign at another audience while it is still an unlaunched draft and replaces its existing recipients; after launch it answers `409`. A sender edit to a draft that launches while the edit is being saved also answers `409`. A changed `scheduledAt` after launch answers `409`; use reschedule to move a scheduled start. A non-null `scheduledAt` must be integer Unix milliseconds from zero through the JavaScript Date maximum. `sendWindow` changes only while the campaign is a draft or paused; a running campaign answers `409`, so pause it first. An edit to a paused campaign also answers `409` while sends are in progress; retry after they settle. A stalled send that has not reached the provider can be recovered on a later edit attempt. If delivery may have started, the edit waits for a definite result rather than sending the recipient again. A new window applies to the remaining recipients when the campaign resumes.\n\nRequires `campaigns:manage`. Project tokens only access resources in their own project.", "x-audience": "public", "x-polymorfa-resource-boundary": "project", "x-excluded-from-public": false, @@ -16000,6 +19516,213 @@ } } ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UpdatePlatformCampaignRequest" + } + } + } + }, + "responses": { + "200": { + "description": "Success", + "headers": { + "X-Request-Id": { + "schema": { + "type": "string" + }, + "description": "Stable request correlation identifier." + }, + "Polymorfa-Version": { + "schema": { + "type": "string" + }, + "description": "API version applied to this response." + } + }, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": false, + "properties": { + "data": { + "$ref": "#/components/schemas/PlatformCampaign" + } + }, + "required": [ + "data" + ] + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "402": { + "$ref": "#/components/responses/PaymentRequired" + }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "409": { + "$ref": "#/components/responses/Conflict" + }, + "503": { + "$ref": "#/components/responses/CampaignEligibilityUnavailable" + } + } + }, + "delete": { + "operationId": "deleteCampaign", + "tags": [ + "campaigns" + ], + "summary": "Delete campaign", + "description": "Deletes a draft, completed, failed, cancelled or archived campaign. Any other campaign is refused with `409`; stop it first. A completed or failed campaign with a final event pending delivery or an active delivery run is also refused with `409`; retry after both finish.\n\nRequires `campaigns:manage`. Project tokens only access resources in their own project.", + "x-audience": "public", + "x-polymorfa-resource-boundary": "project", + "x-excluded-from-public": false, + "x-required-scope": "campaigns:manage", + "security": [ + { + "ApiKeyAuth": [] + }, + { + "ConsoleSession": [] + } + ], + "parameters": [ + { + "name": "campaignId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "projectId", + "in": "query", + "required": false, + "description": "Project the campaign belongs to. Required with a team API key; a project token is bound to its own project and omits it.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "headers": { + "X-Request-Id": { + "schema": { + "type": "string" + }, + "description": "Stable request correlation identifier." + }, + "Polymorfa-Version": { + "schema": { + "type": "string" + }, + "description": "API version applied to this response." + } + }, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": false, + "properties": { + "data": { + "type": "object", + "additionalProperties": false, + "required": [ + "removed" + ], + "properties": { + "removed": { + "type": "boolean", + "enum": [ + true + ] + } + } + } + }, + "required": [ + "data" + ] + } + } + } + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, + "404": { + "$ref": "#/components/responses/NotFound" + }, + "409": { + "$ref": "#/components/responses/Conflict" + } + } + } + }, + "/platform/campaigns/{campaignId}/launch": { + "post": { + "operationId": "launchPlatformCampaign", + "tags": [ + "campaigns" + ], + "summary": "Launch campaign", + "description": "Starts a draft campaign. It begins sending at `scheduledAt`, or right away when no start time is set. Launch checks the stored sender selection against live plans: `402 campaigns_not_entitled` when a selected number is missing or its plan does not include Campaigns, or when an unselected campaign's project has no such number; `400 invalid_parameter` on `senderConfig.sessionIds` when a selected number is not connected or the selection mixes sending types; `400 campaign_throughput_capped` when a requested throughput is above its ceiling. `409` means the campaign is not a draft, is already launched and waiting for its start time, or its sending numbers changed during the launch.\n\nRequires `campaigns:manage`. Project tokens only access resources in their own project.", + "x-audience": "public", + "x-polymorfa-resource-boundary": "project", + "x-excluded-from-public": false, + "x-required-scope": "campaigns:manage", + "security": [ + { + "ApiKeyAuth": [] + }, + { + "ConsoleSession": [] + } + ], + "parameters": [ + { + "name": "campaignId", + "in": "path", + "required": true, + "schema": { + "type": "string" + } + }, + { + "name": "Idempotency-Key", + "in": "header", + "required": false, + "description": "Optional unique key, 1 to 255 bytes, that makes this request safe to retry for 24 hours. A retry with the same key, credential, `Polymorfa-Version` and request never runs it again: it returns `409 idempotency_completed` when the original succeeded, or the original server error. Reusing the key for a different request returns `409 idempotency_conflict`; a retry while the original is still running returns `409 idempotency_in_progress`, and a retry after an unrecorded outcome returns `409 idempotency_outcome_unknown`.", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 255 + } + } + ], "requestBody": { "required": false, "content": { @@ -16036,7 +19759,36 @@ "properties": { "data": { "type": "object", - "additionalProperties": true + "additionalProperties": true, + "required": [ + "id", + "status", + "operationId" + ], + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "status": { + "type": "string", + "enum": [ + "draft", + "running", + "paused", + "cancelling", + "cancelled", + "completed", + "failed", + "archived" + ] + }, + "operationId": { + "type": "string", + "format": "uuid", + "description": "The operation that carries this lifecycle action." + } + } } }, "required": [ @@ -16068,14 +19820,16 @@ "$ref": "#/components/responses/CampaignEligibilityUnavailable" } } - }, - "delete": { - "operationId": "deleteCampaign", + } + }, + "/platform/campaigns/{campaignId}/pause": { + "post": { + "operationId": "pausePlatformCampaign", "tags": [ "campaigns" ], - "summary": "Delete campaign", - "description": "Deletes a draft, completed, failed, cancelled or archived campaign. Any other campaign is refused with `409`; stop it first. A completed or failed campaign with a final event pending delivery or an active delivery run is also refused with `409`; retry after both finish.\n\nRequires `campaigns:manage`. Project tokens only access resources in their own project.", + "summary": "Pause campaign", + "description": "Pauses a running campaign. Sends already in progress can still finish. `409` means the campaign is not running or has no active delivery run to pause.\n\nRequires `campaigns:manage`. Project tokens only access resources in their own project.", "x-audience": "public", "x-polymorfa-resource-boundary": "project", "x-excluded-from-public": false, @@ -16098,15 +19852,28 @@ } }, { - "name": "projectId", - "in": "query", + "name": "Idempotency-Key", + "in": "header", "required": false, - "description": "Project the campaign belongs to. Required with a team API key; a project token is bound to its own project and omits it.", + "description": "Optional unique key, 1 to 255 bytes, that makes this request safe to retry for 24 hours. A retry with the same key, credential, `Polymorfa-Version` and request never runs it again: it returns `409 idempotency_completed` when the original succeeded, or the original server error. Reusing the key for a different request returns `409 idempotency_conflict`; a retry while the original is still running returns `409 idempotency_in_progress`, and a retry after an unrecorded outcome returns `409 idempotency_outcome_unknown`.", "schema": { - "type": "string" + "type": "string", + "minLength": 1, + "maxLength": 255 } } ], + "requestBody": { + "required": false, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": true + } + } + } + }, "responses": { "200": { "description": "Success", @@ -16132,16 +19899,34 @@ "properties": { "data": { "type": "object", - "additionalProperties": false, + "additionalProperties": true, "required": [ - "removed" + "id", + "status", + "operationId" ], "properties": { - "removed": { - "type": "boolean", + "id": { + "type": "string", + "format": "uuid" + }, + "status": { + "type": "string", "enum": [ - true + "draft", + "running", + "paused", + "cancelling", + "cancelled", + "completed", + "failed", + "archived" ] + }, + "operationId": { + "type": "string", + "format": "uuid", + "description": "The operation that carries this lifecycle action." } } } @@ -16168,14 +19953,14 @@ } } }, - "/platform/campaigns/{campaignId}/launch": { + "/platform/campaigns/{campaignId}/resume": { "post": { - "operationId": "launchPlatformCampaign", + "operationId": "resumePlatformCampaign", "tags": [ "campaigns" ], - "summary": "Launch campaign", - "description": "Starts a draft campaign. It begins sending at `scheduledAt`, or right away when no start time is set. Launch checks the stored sender selection against live plans: `402 campaigns_not_entitled` when a selected number is missing or its plan does not include Campaigns, or when an unselected campaign's project has no such number; `400 invalid_parameter` on `senderConfig.sessionIds` when a selected number is not connected or the selection mixes sending types; `400 campaign_throughput_capped` when a requested throughput is above its ceiling. `409` means the campaign is not a draft, is already launched and waiting for its start time, or its sending numbers changed during the launch.\n\nRequires `campaigns:manage`. Project tokens only access resources in their own project.", + "summary": "Resume campaign", + "description": "Resumes a paused campaign. Its remaining recipients are sent after the same checks as before the pause. `409` means the campaign is not paused or has no active delivery run to resume.\n\nRequires `campaigns:manage`. Project tokens only access resources in their own project.", "x-audience": "public", "x-polymorfa-resource-boundary": "project", "x-excluded-from-public": false, @@ -16284,15 +20069,9 @@ } } }, - "400": { - "$ref": "#/components/responses/BadRequest" - }, "401": { "$ref": "#/components/responses/Unauthorized" }, - "402": { - "$ref": "#/components/responses/PaymentRequired" - }, "403": { "$ref": "#/components/responses/Forbidden" }, @@ -16301,21 +20080,18 @@ }, "409": { "$ref": "#/components/responses/Conflict" - }, - "503": { - "$ref": "#/components/responses/CampaignEligibilityUnavailable" } } } }, - "/platform/campaigns/{campaignId}/pause": { + "/platform/campaigns/{campaignId}/stop": { "post": { - "operationId": "pausePlatformCampaign", + "operationId": "stopPlatformCampaign", "tags": [ "campaigns" ], - "summary": "Pause campaign", - "description": "Pauses a running campaign. Sends already in progress can still finish. `409` means the campaign is not running or has no active delivery run to pause.\n\nRequires `campaigns:manage`. Project tokens only access resources in their own project.", + "summary": "Stop campaign", + "description": "Cancels a draft, running or paused campaign. Recipients not yet sent are not sent. The campaign moves to `cancelling` and then to `cancelled` once sends already in progress finish; a campaign with no active delivery run is `cancelled` immediately and `operationId` is null. `409` means the campaign is already finished or cancelled.\n\nRequires `campaigns:manage`. Project tokens only access resources in their own project.", "x-audience": "public", "x-polymorfa-resource-boundary": "project", "x-excluded-from-public": false, @@ -16412,7 +20188,8 @@ "operationId": { "type": "string", "format": "uuid", - "description": "The operation that carries this lifecycle action." + "nullable": true, + "description": "The operation that stops the campaign. Null when the campaign had no active delivery run and was cancelled immediately." } } } @@ -16439,14 +20216,14 @@ } } }, - "/platform/campaigns/{campaignId}/resume": { + "/platform/campaigns/{campaignId}/reschedule": { "post": { - "operationId": "resumePlatformCampaign", + "operationId": "reschedulePlatformCampaign", "tags": [ "campaigns" ], - "summary": "Resume campaign", - "description": "Resumes a paused campaign. Its remaining recipients are sent after the same checks as before the pause. `409` means the campaign is not paused or has no active delivery run to resume.\n\nRequires `campaigns:manage`. Project tokens only access resources in their own project.", + "summary": "Reschedule campaign", + "description": "Moves the start of a launched campaign that is still waiting for it. Set `scheduledAt` to a later or earlier time, or to `null` to start now. The response returns the campaign with its new `scheduledAt`; sending the current start time again changes nothing. A suspended team is refused with `403 bansafe_org_suspended`. `409` means the campaign has already started, is finished or being stopped, or was never launched; launch a draft with `scheduledAt` instead.\n\nRequires `campaigns:manage`. Project tokens only access resources in their own project.", "x-audience": "public", "x-polymorfa-resource-boundary": "project", "x-excluded-from-public": false, @@ -16481,12 +20258,11 @@ } ], "requestBody": { - "required": false, + "required": true, "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": true + "$ref": "#/components/schemas/ReschedulePlatformCampaignRequest" } } } @@ -16555,6 +20331,9 @@ } } }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, "401": { "$ref": "#/components/responses/Unauthorized" }, @@ -16570,14 +20349,14 @@ } } }, - "/platform/campaigns/{campaignId}/stop": { + "/platform/campaigns/{campaignId}/archive": { "post": { - "operationId": "stopPlatformCampaign", + "operationId": "archiveCampaign", "tags": [ "campaigns" ], - "summary": "Stop campaign", - "description": "Cancels a draft, running or paused campaign. Recipients not yet sent are not sent. The campaign moves to `cancelling` and then to `cancelled` once sends already in progress finish; a campaign with no active delivery run is `cancelled` immediately and `operationId` is null. `409` means the campaign is already finished or cancelled.\n\nRequires `campaigns:manage`. Project tokens only access resources in their own project.", + "summary": "Archive campaign", + "description": "Archives a completed, failed or cancelled campaign. Other states return `409`. A final event pending delivery or an active delivery run also returns `409`; retry after both finish.\n\nRequires `campaigns:manage`. Project tokens only access resources in their own project.", "x-audience": "public", "x-polymorfa-resource-boundary": "project", "x-excluded-from-public": false, @@ -16598,17 +20377,6 @@ "schema": { "type": "string" } - }, - { - "name": "Idempotency-Key", - "in": "header", - "required": false, - "description": "Optional unique key, 1 to 255 bytes, that makes this request safe to retry for 24 hours. A retry with the same key, credential, `Polymorfa-Version` and request never runs it again: it returns `409 idempotency_completed` when the original succeeded, or the original server error. Reusing the key for a different request returns `409 idempotency_conflict`; a retry while the original is still running returns `409 idempotency_in_progress`, and a retry after an unrecorded outcome returns `409 idempotency_outcome_unknown`.", - "schema": { - "type": "string", - "minLength": 1, - "maxLength": 255 - } } ], "requestBody": { @@ -16647,37 +20415,7 @@ "properties": { "data": { "type": "object", - "additionalProperties": true, - "required": [ - "id", - "status", - "operationId" - ], - "properties": { - "id": { - "type": "string", - "format": "uuid" - }, - "status": { - "type": "string", - "enum": [ - "draft", - "running", - "paused", - "cancelling", - "cancelled", - "completed", - "failed", - "archived" - ] - }, - "operationId": { - "type": "string", - "format": "uuid", - "nullable": true, - "description": "The operation that stops the campaign. Null when the campaign had no active delivery run and was cancelled immediately." - } - } + "additionalProperties": true } }, "required": [ @@ -16702,14 +20440,14 @@ } } }, - "/platform/campaigns/{campaignId}/reschedule": { + "/platform/campaigns/{campaignId}/duplicate": { "post": { - "operationId": "reschedulePlatformCampaign", + "operationId": "duplicateCampaign", "tags": [ "campaigns" ], - "summary": "Reschedule campaign", - "description": "Moves the start of a launched campaign that is still waiting for it. Set `scheduledAt` to a later or earlier time, or to `null` to start now. The response returns the campaign with its new `scheduledAt`; sending the current start time again changes nothing. A suspended team is refused with `403 bansafe_org_suspended`. `409` means the campaign has already started, is finished or being stopped, or was never launched; launch a draft with `scheduledAt` instead.\n\nRequires `campaigns:manage`. Project tokens only access resources in their own project.", + "summary": "Duplicate campaign", + "description": "Public Management API. Requires `campaigns:manage`. Project tokens only access resources in their own project.", "x-audience": "public", "x-polymorfa-resource-boundary": "project", "x-excluded-from-public": false, @@ -16730,25 +20468,15 @@ "schema": { "type": "string" } - }, - { - "name": "Idempotency-Key", - "in": "header", - "required": false, - "description": "Optional unique key, 1 to 255 bytes, that makes this request safe to retry for 24 hours. A retry with the same key, credential, `Polymorfa-Version` and request never runs it again: it returns `409 idempotency_completed` when the original succeeded, or the original server error. Reusing the key for a different request returns `409 idempotency_conflict`; a retry while the original is still running returns `409 idempotency_in_progress`, and a retry after an unrecorded outcome returns `409 idempotency_outcome_unknown`.", - "schema": { - "type": "string", - "minLength": 1, - "maxLength": 255 - } } ], "requestBody": { - "required": true, + "required": false, "content": { "application/json": { "schema": { - "$ref": "#/components/schemas/ReschedulePlatformCampaignRequest" + "type": "object", + "additionalProperties": true } } } @@ -16773,245 +20501,31 @@ "content": { "application/json": { "schema": { - "type": "object", - "additionalProperties": false, - "properties": { - "data": { - "type": "object", - "additionalProperties": true, - "required": [ - "id", - "status", - "operationId" - ], - "properties": { - "id": { - "type": "string", - "format": "uuid" - }, - "status": { - "type": "string", - "enum": [ - "draft", - "running", - "paused", - "cancelling", - "cancelled", - "completed", - "failed", - "archived" - ] - }, - "operationId": { - "type": "string", - "format": "uuid", - "description": "The operation that carries this lifecycle action." - } - } - } - }, - "required": [ - "data" - ] + "$ref": "#/components/schemas/DataEnvelope" } } } }, - "400": { - "$ref": "#/components/responses/BadRequest" - }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "409": { - "$ref": "#/components/responses/Conflict" + "501": { + "$ref": "#/components/responses/NotImplemented" } } } }, - "/platform/campaigns/{campaignId}/archive": { + "/platform/campaigns/{campaignId}/requeue": { "post": { - "operationId": "archiveCampaign", + "operationId": "requeueFailedCampaign", "tags": [ "campaigns" ], - "summary": "Archive campaign", - "description": "Archives a completed, failed or cancelled campaign. Other states return `409`. A final event pending delivery or an active delivery run also returns `409`; retry after both finish.\n\nRequires `campaigns:manage`. Project tokens only access resources in their own project.", - "x-audience": "public", - "x-polymorfa-resource-boundary": "project", - "x-excluded-from-public": false, - "x-required-scope": "campaigns:manage", - "security": [ - { - "ApiKeyAuth": [] - }, - { - "ConsoleSession": [] - } - ], - "parameters": [ - { - "name": "campaignId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": false, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true - } - } - } - }, - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "schema": { - "type": "string" - }, - "description": "Stable request correlation identifier." - }, - "Polymorfa-Version": { - "schema": { - "type": "string" - }, - "description": "API version applied to this response." - } - }, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": false, - "properties": { - "data": { - "type": "object", - "additionalProperties": true - } - }, - "required": [ - "data" - ] - } - } - } - }, - "401": { - "$ref": "#/components/responses/Unauthorized" - }, - "403": { - "$ref": "#/components/responses/Forbidden" - }, - "404": { - "$ref": "#/components/responses/NotFound" - }, - "409": { - "$ref": "#/components/responses/Conflict" - } - } - } - }, - "/platform/campaigns/{campaignId}/duplicate": { - "post": { - "operationId": "duplicateCampaign", - "tags": [ - "campaigns" - ], - "summary": "Duplicate campaign", - "description": "Public Management API. Requires `campaigns:manage`. Project tokens only access resources in their own project.", - "x-audience": "public", - "x-polymorfa-resource-boundary": "project", - "x-excluded-from-public": false, - "x-required-scope": "campaigns:manage", - "security": [ - { - "ApiKeyAuth": [] - }, - { - "ConsoleSession": [] - } - ], - "parameters": [ - { - "name": "campaignId", - "in": "path", - "required": true, - "schema": { - "type": "string" - } - } - ], - "requestBody": { - "required": false, - "content": { - "application/json": { - "schema": { - "type": "object", - "additionalProperties": true - } - } - } - }, - "responses": { - "200": { - "description": "Success", - "headers": { - "X-Request-Id": { - "schema": { - "type": "string" - }, - "description": "Stable request correlation identifier." - }, - "Polymorfa-Version": { - "schema": { - "type": "string" - }, - "description": "API version applied to this response." - } - }, - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/DataEnvelope" - } - } - } - }, - "401": { - "$ref": "#/components/responses/Unauthorized" - }, - "403": { - "$ref": "#/components/responses/Forbidden" - }, - "501": { - "$ref": "#/components/responses/NotImplemented" - } - } - } - }, - "/platform/campaigns/{campaignId}/requeue": { - "post": { - "operationId": "requeueFailedCampaign", - "tags": [ - "campaigns" - ], - "summary": "Requeue failed campaign", - "description": "Public Management API. Requires `campaigns:manage`. Project tokens only access resources in their own project.", + "summary": "Requeue failed campaign", + "description": "Public Management API. Requires `campaigns:manage`. Project tokens only access resources in their own project.", "x-audience": "public", "x-polymorfa-resource-boundary": "project", "x-excluded-from-public": false, @@ -30451,6 +33965,98 @@ } } }, + "/platform/calls/{callId}": { + "get": { + "operationId": "getCallRecord", + "tags": [ + "calls" + ], + "summary": "Get a call record and history", + "description": "Returns one stored call with its participant and connection lifetimes, bounded event history, media measurements and app-reported diagnostics. Requires sessions:read. Project credentials can read only the call's original owning project, including after a number is deleted. Unknown measurements are null. Webhook deliveries require the separate webhook-deliveries:read endpoints. This read does not retrieve audio, video or event payloads and does not control a live call.\n\nRequires `sessions:read`. Project tokens only access resources in their own project.", + "x-audience": "public", + "x-polymorfa-resource-boundary": "project", + "x-excluded-from-public": false, + "x-required-scope": "sessions:read", + "security": [ + { + "ApiKeyAuth": [] + }, + { + "ConsoleSession": [] + } + ], + "parameters": [ + { + "name": "callId", + "in": "path", + "required": true, + "description": "Call identifier.", + "schema": { + "type": "string", + "minLength": 1, + "maxLength": 128, + "pattern": "^[\\x21-\\x7e]+$" + } + }, + { + "name": "projectId", + "in": "query", + "required": false, + "description": "Project to read. Omit to total every project in the organization.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "Success", + "headers": { + "X-Request-Id": { + "schema": { + "type": "string" + }, + "description": "Stable request correlation identifier." + }, + "Polymorfa-Version": { + "schema": { + "type": "string" + }, + "description": "API version applied to this response." + } + }, + "content": { + "application/json": { + "schema": { + "type": "object", + "additionalProperties": false, + "properties": { + "data": { + "$ref": "#/components/schemas/CallRecordDetail" + } + }, + "required": [ + "data" + ] + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "403": { + "$ref": "#/components/responses/Forbidden" + }, + "404": { + "$ref": "#/components/responses/NotFound" + } + } + } + }, "/console/calls": { "get": { "operationId": "listCalls", @@ -31420,6 +35026,11 @@ "type": "boolean", "default": false, "description": "Explicit measurement consent; the server also requires telemetry configuration." + }, + "projectId": { + "type": "string", + "format": "uuid", + "description": "Optional active project in the signed-in team. Required when checking a project-targeted release." } } } @@ -31604,6 +35215,11 @@ "type": "string", "nullable": true, "maxLength": 100 + }, + "projectId": { + "type": "string", + "format": "uuid", + "description": "Optional active project in the signed-in team for a project-targeted release." } }, "required": [ @@ -31805,6 +35421,65 @@ } }, "schemas": { + "DashboardUnauthorizedError": { + "type": "object", "additionalProperties": false, "required": ["error"], + "properties": {"error": {"type": "string", "enum": ["invalid dashboard token"]}} + }, + "AccountCloudHealthResponse": { + "type": "object", "additionalProperties": false, "required": ["success", "data"], + "properties": { + "success": {"type": "boolean", "enum": [true]}, + "data": {"$ref": "#/components/schemas/AccountCloudHealthView"} + } + }, + "AccountCloudHealthView": { + "type": "object", "additionalProperties": false, + "required": ["session", "phone", "appId", "wabaId", "connection", "credentials", "account", "readiness", "diagnostic"], + "properties": { + "session": {"type": "string"}, + "phone": {"type": "string", "nullable": true}, + "appId": {"type": "string", "nullable": true}, + "wabaId": {"type": "string", "nullable": true}, + "connection": {"type": "object", "additionalProperties": false, "required": ["status", "connected", "sendingPaused"], "properties": { + "status": {"type": "string"}, "connected": {"type": "boolean", "description": "Whether the Official API connection is connected. A Hybrid sending pause does not change this value."}, + "sendingPaused": {"type": "boolean", "description": "Whether Hybrid sending is paused for this Number. Always false for Numbers without a Hybrid Link."} + }}, + "credentials": {"type": "object", "additionalProperties": false, + "required": ["status", "tokenStatus", "missingPermissions", "phoneRegistration", "webhookSubscription", "failureCode"], + "properties": { + "status": {"type": "string", "enum": ["pending", "healthy", "action_required", "unknown"]}, + "tokenStatus": {"type": "string", "enum": ["valid", "expired", "invalid", "unknown"]}, + "missingPermissions": {"type": "array", "items": {"type": "string"}}, + "phoneRegistration": {"type": "string", "enum": ["registered", "not_registered", "unknown"]}, + "webhookSubscription": {"type": "string", "enum": ["subscribed", "not_subscribed", "unknown"]}, + "failureCode": {"type": "string", "nullable": true} + } + }, + "account": {"type": "object", "additionalProperties": false, + "required": ["verifiedName", "qualityRating", "messagingLimitTier", "displayNameReview", "payment", "restrictions"], + "properties": { + "verifiedName": {"type": "string", "nullable": true}, + "qualityRating": {"type": "string", "enum": ["GREEN", "YELLOW", "RED", "NA"], "nullable": true}, + "messagingLimitTier": {"type": "string", "nullable": true}, + "displayNameReview": {"type": "string", "enum": ["unverified"]}, + "payment": {"type": "string", "enum": ["unverified"]}, + "restrictions": {"type": "string", "enum": ["unverified"]} + } + }, + "readiness": {"type": "object", "additionalProperties": false, + "required": ["connection", "webhook", "messaging", "nextAction"], + "properties": { + "connection": {"type": "string", "enum": ["connected", "not_connected"]}, + "webhook": {"type": "string", "enum": ["configured", "not_configured", "unknown"]}, + "messaging": {"type": "string", "enum": ["not_ready", "unverified"]}, + "nextAction": {"type": "string", "enum": ["reconnect_number", "reauthorize", "complete_registration", "check_webhook_subscription", "review_account_quality", "verify_messaging_and_webhooks", "retry_health_check", "resume_hybrid_sending", "contact_support"]} + } + }, + "diagnostic": {"type": "object", "additionalProperties": false, "required": ["id", "observedAt"], + "properties": {"id": {"type": "string", "format": "uuid", "nullable": true}, "observedAt": {"type": "string", "format": "date-time", "nullable": true}} + } + } + }, "Function": { "type": "object", "additionalProperties": false, @@ -37597,6 +41272,14 @@ "type": "number", "description": "Scheduled start time in Unix milliseconds." }, + "sendWindow": { + "nullable": true, + "allOf": [ + { + "$ref": "#/components/schemas/CampaignSendWindowRequest" + } + ] + }, "recipients": { "type": "array", "minItems": 0, @@ -37621,6 +41304,281 @@ "name" ] }, + "UpdatePlatformCampaignRequest": { + "type": "object", + "additionalProperties": true, + "description": "Fields to change. Omitted fields keep their stored value.", + "properties": { + "name": { + "type": "string", + "minLength": 1, + "maxLength": 200 + }, + "projectId": { + "type": "string", + "format": "uuid", + "description": "Required when using a team API key. Project tokens derive the project from the credential." + }, + "recipientListId": { + "type": "string", + "format": "uuid", + "nullable": true, + "description": "Audience to send to. Only while the campaign is an unlaunched draft." + }, + "senderConfig": { + "type": "object", + "additionalProperties": true + }, + "scheduledAt": { + "type": "integer", + "minimum": 0, + "maximum": 8640000000000000, + "nullable": true, + "description": "Scheduled start time in Unix milliseconds. Only while the campaign is an unlaunched draft." + }, + "sendWindow": { + "nullable": true, + "allOf": [ + { + "$ref": "#/components/schemas/CampaignSendWindowRequest" + } + ], + "description": "When the campaign may send. `null` removes the window. Only while the campaign is a draft or paused." + } + } + }, + "CampaignSendWindowRange": { + "type": "object", + "additionalProperties": false, + "properties": { + "start": { + "type": "string", + "pattern": "^([01][0-9]|2[0-3]):[0-5][0-9]$", + "description": "Local start time, `HH:MM` in 24-hour time, inclusive.", + "example": "09:00" + }, + "end": { + "type": "string", + "pattern": "^(([01][0-9]|2[0-3]):[0-5][0-9]|24:00)$", + "description": "Local end time, `HH:MM` in 24-hour time or `24:00`, exclusive. Must be later than `start`.", + "example": "18:00" + } + }, + "required": [ + "start", + "end" + ] + }, + "CampaignSendWindowRequest": { + "type": "object", + "additionalProperties": false, + "description": "When the campaign may send. Recipients outside the window stay `queued` and are sent when it next opens.", + "properties": { + "timeZone": { + "type": "string", + "minLength": 1, + "maxLength": 64, + "description": "IANA time zone the window is written in, such as `America/Sao_Paulo`. When omitted, the team's time zone is stored, or `UTC` when the team has none.", + "example": "America/Sao_Paulo" + }, + "days": { + "type": "array", + "minItems": 1, + "maxItems": 7, + "uniqueItems": true, + "items": { + "type": "string", + "enum": [ + "monday", + "tuesday", + "wednesday", + "thursday", + "friday", + "saturday", + "sunday" + ] + }, + "description": "Weekdays the campaign may send on, in the window's time zone." + }, + "hours": { + "type": "array", + "minItems": 1, + "maxItems": 4, + "items": { + "$ref": "#/components/schemas/CampaignSendWindowRange" + }, + "description": "Local time ranges the campaign may send in on those days. Ranges must not overlap. A range cannot cross midnight; use two ranges instead." + }, + "recipientTimeZone": { + "type": "boolean", + "default": false, + "description": "Evaluate the window in each recipient's own time zone. The zone comes from the recipient variable named by `timeZoneVariable` when it holds a valid IANA zone, otherwise from the phone number's country when that country uses one UTC offset, otherwise `timeZone`." + }, + "timeZoneVariable": { + "type": "string", + "pattern": "^[A-Za-z0-9_][A-Za-z0-9_.-]{0,63}$", + "default": "timeZone", + "description": "Recipient variable holding the recipient's IANA time zone, used when `recipientTimeZone` is true. Launch rejects more than 5,000 distinct nonempty values with `400 invalid_parameter`." + } + }, + "required": [ + "days", + "hours" + ] + }, + "CampaignSendWindow": { + "type": "object", + "additionalProperties": false, + "description": "When the campaign may send. Recipients outside the window stay `queued` and are sent when it next opens.", + "properties": { + "timeZone": { + "type": "string", + "description": "IANA time zone the window is written in." + }, + "days": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "monday", + "tuesday", + "wednesday", + "thursday", + "friday", + "saturday", + "sunday" + ] + } + }, + "hours": { + "type": "array", + "items": { + "$ref": "#/components/schemas/CampaignSendWindowRange" + } + }, + "recipientTimeZone": { + "type": "boolean" + }, + "timeZoneVariable": { + "type": "string" + } + }, + "required": [ + "timeZone", + "days", + "hours", + "recipientTimeZone", + "timeZoneVariable" + ] + }, + "PlatformCampaign": { + "type": "object", + "additionalProperties": true, + "description": "A campaign returned by Platform list, get, create and update. `sendWindow` is null when sending is unrestricted by local hours.", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string" + }, + "status": { + "type": "string" + }, + "templateId": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "recipientListId": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "recipientCount": { + "type": "integer", + "minimum": 0 + }, + "sentCount": { + "type": "integer", + "minimum": 0 + }, + "deliveredCount": { + "type": "integer", + "minimum": 0 + }, + "readCount": { + "type": "integer", + "minimum": 0 + }, + "failedCount": { + "type": "integer", + "minimum": 0 + }, + "skippedCount": { + "type": "integer", + "minimum": 0 + }, + "scheduledAt": { + "type": "integer", + "nullable": true, + "description": "Scheduled start in Unix milliseconds." + }, + "launchedAt": { + "type": "integer", + "nullable": true, + "description": "Launch time in Unix milliseconds." + }, + "completedAt": { + "type": "integer", + "nullable": true, + "description": "Completion time in Unix milliseconds." + }, + "createdAt": { + "type": "integer", + "description": "Creation time in Unix milliseconds." + }, + "updatedAt": { + "type": "integer", + "description": "Last update in Unix milliseconds." + }, + "sendWindow": { + "nullable": true, + "allOf": [ + { + "$ref": "#/components/schemas/CampaignSendWindow" + } + ] + }, + "composerBlueprint": {}, + "messages": {}, + "audienceRef": {}, + "senderConfig": {}, + "complianceConfig": {}, + "variants": {}, + "variantStrategy": {} + }, + "required": [ + "id", + "name", + "status", + "templateId", + "recipientListId", + "recipientCount", + "sentCount", + "deliveredCount", + "readCount", + "failedCount", + "skippedCount", + "scheduledAt", + "launchedAt", + "completedAt", + "createdAt", + "updatedAt", + "sendWindow" + ] + }, "ReschedulePlatformCampaignRequest": { "type": "object", "additionalProperties": false, @@ -44503,7 +48461,7 @@ "fromUser": { "type": "string", "nullable": true, - "description": "User part of the From header on calls sent to your PBX, or null to send `whatsapp`." + "description": "Optional override for the From header user on calls sent to your PBX. Null uses the incoming caller identity: E.164 phone number when known, a non-phone LID address otherwise, or `whatsapp` when unknown." } }, "required": [ @@ -44741,7 +48699,7 @@ "type": "string", "nullable": true, "pattern": "^[A-Za-z0-9+._-]{1,64}$", - "description": "User part of the From header on calls sent to your PBX. Null sends `whatsapp`, which is also the default for a new trunk." + "description": "Optional override for the From header user on calls sent to your PBX. Omit or set null to use the incoming caller identity: E.164 phone number when known, a non-phone LID address otherwise, or `whatsapp` when unknown." } }, "required": [ @@ -44909,7 +48867,7 @@ "type": "string", "nullable": true, "pattern": "^[A-Za-z0-9+._-]{1,64}$", - "description": "User part of the From header on calls sent to your PBX. Null sends `whatsapp`, which is also the default for a new trunk." + "description": "Optional override for the From header user on calls sent to your PBX. Omit or set null to use the incoming caller identity: E.164 phone number when known, a non-phone LID address otherwise, or `whatsapp` when unknown." } }, "additionalProperties": false, @@ -47378,6 +51336,541 @@ "data", "docs" ] + }, + "CallRecordEndReason": { + "type": "object", + "additionalProperties": false, + "description": "Why the call ended or was not answered. `code` is `other` for an unrecognized reason.", + "properties": { + "code": { + "type": "string", + "pattern": "^[a-z][a-z0-9_]{0,63}$", + "example": "user_hangup" + }, + "label": { + "type": "string", + "example": "Hung up" + } + }, + "required": [ + "code", + "label" + ] + }, + "CallRecordDetail": { + "type": "object", + "additionalProperties": false, + "properties": { + "call": { + "type": "object", + "additionalProperties": false, + "properties": { + "callId": { + "type": "string" + }, + "sessionId": { + "type": "string", + "description": "Session name." + }, + "projectId": { + "type": "string", + "format": "uuid", + "nullable": true + }, + "direction": { + "type": "string", + "enum": [ + "inbound", + "outbound" + ] + }, + "state": { + "type": "string", + "enum": [ + "offered", + "accepted", + "rejected", + "missed", + "ended" + ], + "description": "Call outcome. `offered` and `accepted` calls may still be live. `rejected` is an incoming call a participant declined. `missed` is an incoming call that rang without an answer. Both keep that state after the call ends. `ended` covers every other finished call: answered calls, outgoing calls, and incoming calls the service could not host. `endReason` gives the cause." + }, + "live": { + "type": "boolean" + }, + "backend": { + "type": "string", + "description": "How the number reaches WhatsApp for this call." + }, + "hasVideo": { + "type": "boolean" + }, + "peerRef": { + "type": "string", + "nullable": true, + "description": "Team-scoped pseudonym of the remote party." + }, + "startedAt": { + "type": "string", + "format": "date-time" + }, + "connectedAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "endedAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "durationSeconds": { + "type": "integer", + "nullable": true + }, + "endReason": { + "nullable": true, + "allOf": [ + { + "$ref": "#/components/schemas/CallRecordEndReason" + } + ] + }, + "answeredBy": { + "type": "string", + "nullable": true, + "description": "Participant reference that answered: `client:` or `server:`." + }, + "exclusive": { + "type": "boolean", + "nullable": true, + "description": "Whether the answering participant claimed the call. Null when unknown." + } + }, + "required": [ + "callId", + "sessionId", + "projectId", + "direction", + "state", + "live", + "backend", + "hasVideo", + "peerRef", + "startedAt", + "connectedAt", + "endedAt", + "durationSeconds", + "endReason", + "answeredBy", + "exclusive" + ] + }, + "participants": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "id": { + "type": "string", + "pattern": "^[1-9][0-9]{0,18}$", + "description": "Public participant id, as call.participant_* webhooks report it." + }, + "state": { + "type": "string", + "enum": [ + "invited", + "ringing", + "connected", + "left" + ] + }, + "firstSeenAt": { + "type": "string", + "format": "date-time" + }, + "updatedAt": { + "type": "string", + "format": "date-time" + }, + "leftReason": { + "type": "string", + "nullable": true + } + }, + "required": [ + "id", + "state", + "firstSeenAt", + "updatedAt", + "leftReason" + ] + } + }, + "connections": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "id": { + "type": "string", + "pattern": "^[A-Za-z0-9_-]{8,64}$" + }, + "participant": { + "type": "string", + "pattern": "^(client|server):[A-Za-z0-9._:@-]{1,128}$" + }, + "transport": { + "type": "string", + "enum": [ + "webrtc", + "socket", + "sip", + "unknown" + ] + }, + "joinedAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "leftAt": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "reason": { + "type": "string", + "enum": [ + "left", + "replaced", + "claimed", + "call_ended", + "sip_busy", + "sip_declined", + "sip_no_answer", + "sip_unavailable", + "sip_auth_failed", + null + ], + "nullable": true + } + }, + "required": [ + "id", + "participant", + "transport", + "joinedAt", + "leftAt", + "reason" + ] + } + }, + "telemetry": { + "type": "object", + "additionalProperties": false, + "description": "Media measurements reported by the call service. Measurements that were not taken are null.", + "properties": { + "status": { + "type": "string", + "enum": [ + "reported", + "unknown" + ] + }, + "source": { + "type": "string", + "enum": [ + "media_server" + ] + }, + "setupMs": { + "type": "number", + "nullable": true + }, + "ringMs": { + "type": "number", + "nullable": true + }, + "codec": { + "type": "string", + "nullable": true + }, + "jitterMs": { + "type": "number", + "nullable": true + }, + "packetsLost": { + "type": "number", + "nullable": true + }, + "rttMs": { + "type": "number", + "nullable": true + }, + "receivedKbps": { + "type": "number", + "nullable": true + }, + "sentKbps": { + "type": "number", + "nullable": true + } + }, + "required": [ + "status", + "source", + "setupMs", + "ringMs", + "codec", + "jitterMs", + "packetsLost", + "rttMs", + "receivedKbps", + "sentKbps" + ] + }, + "appReports": { + "type": "object", + "additionalProperties": false, + "description": "Diagnostics your app sent with `POST /messaging/voip/calls/{id}/reports`, by connection. Figures the app did not report are null.", + "properties": { + "status": { + "type": "string", + "enum": [ + "reported", + "none" + ], + "description": "`none` when the app sent no report for the call." + }, + "connections": { + "type": "array", + "maxItems": 200, + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "connectionId": { + "type": "string", + "pattern": "^[A-Za-z0-9_-]{8,64}$" + }, + "participant": { + "type": "string", + "pattern": "^(client|server):[A-Za-z0-9._:@-]{1,128}$" + }, + "client": { + "type": "object", + "nullable": true, + "additionalProperties": false, + "description": "The SDK named by the connection's most recent report. Null when that report named none.", + "properties": { + "sdk": { + "type": "string", + "pattern": "^[a-z0-9@/._-]{1,32}$" + }, + "version": { + "type": "string", + "maxLength": 32 + }, + "platform": { + "type": "string", + "enum": [ + "browser", + "node", + "other" + ] + } + }, + "required": [ + "sdk", + "version", + "platform" + ] + }, + "quality": { + "type": "object", + "nullable": true, + "additionalProperties": false, + "description": "The connection's latest quality report. Null when it sent none.", + "properties": { + "reportedAt": { + "type": "string", + "format": "date-time" + }, + "rttMs": { + "type": "integer", + "nullable": true + }, + "jitterMs": { + "type": "integer", + "nullable": true + }, + "packetsLost": { + "type": "integer", + "nullable": true + }, + "packetsReceived": { + "type": "integer", + "nullable": true + }, + "audioCodec": { + "type": "string", + "nullable": true + }, + "videoCodec": { + "type": "string", + "nullable": true + }, + "candidateType": { + "type": "string", + "enum": [ + "host", + "srflx", + "prflx", + "relay", + null + ], + "nullable": true + }, + "reconnects": { + "type": "integer", + "nullable": true + } + }, + "required": [ + "reportedAt", + "rttMs", + "jitterMs", + "packetsLost", + "packetsReceived", + "audioCodec", + "videoCodec", + "candidateType", + "reconnects" + ] + }, + "errors": { + "type": "array", + "maxItems": 50, + "description": "Reported errors, newest first.", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "code": { + "type": "string", + "enum": [ + "media_permission_denied", + "device_not_found", + "device_in_use", + "ice_failed", + "negotiation_failed", + "media_timeout", + "reconnect_exhausted", + "token_refresh_failed", + "unsupported_browser", + "other" + ] + }, + "reportedAt": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "code", + "reportedAt" + ] + } + } + }, + "required": [ + "connectionId", + "participant", + "client", + "quality", + "errors" + ] + } + }, + "truncated": { + "type": "boolean", + "description": "True when the call reached its limit of 200 stored reports or older errors are not shown." + } + }, + "required": [ + "status", + "connections", + "truncated" + ] + }, + "history": { + "type": "object", + "additionalProperties": false, + "properties": { + "events": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "eventId": { + "type": "string", + "description": "Event id, as the `id` of the matching webhook delivery." + }, + "type": { + "type": "string" + }, + "occurredAt": { + "type": "string", + "format": "date-time" + } + }, + "required": [ + "eventId", + "type", + "occurredAt" + ] + } + }, + "truncated": { + "type": "boolean", + "description": "True when the call exceeded the recorded history limit." + } + }, + "required": [ + "events", + "truncated" + ] + }, + "correlation": { + "type": "object", + "additionalProperties": false, + "properties": { + "callId": { + "type": "string" + }, + "sessionId": { + "type": "string" + } + }, + "required": [ + "callId", + "sessionId" + ] + } + }, + "required": [ + "call", + "participants", + "connections", + "telemetry", + "appReports", + "history", + "correlation" + ] } }, "responses": { @@ -47813,6 +52306,29 @@ } } } + }, + "headers": { + "X-RateLimit-Limit": { + "description": "Team request quota per rolling minute, when the per-team request limiter applies.", + "schema": { + "type": "integer", + "minimum": 1 + } + }, + "X-RateLimit-Remaining": { + "description": "Requests left in the per-team quota; zero when this limiter rejects the request.", + "schema": { + "type": "integer", + "minimum": 0 + } + }, + "X-RateLimit-Reset": { + "description": "Unix timestamp in seconds when the oldest counted request leaves the rolling window, when the per-team request limiter applies. Follow Retry-After on a rejected request.", + "schema": { + "type": "integer", + "minimum": 0 + } + } } } } diff --git a/contracts/source.json b/contracts/source.json index 46af627d..2109fe31 100644 --- a/contracts/source.json +++ b/contracts/source.json @@ -1,19 +1,19 @@ { "repository": "polymorfa/polymorfa", - "commit": "2f24c1d62", - "branch": "codex/native-release-capabilities-20260926", + "commit": "5c8014d7d58b4d3e483d9d124ee9d88bd85820f9", + "branch": "dev", "published": false, - "note": "Coordinated with polymorfa/polymorfa PR #388 at source commit 2f24c1d62. Both snapshots are byte-identical to the authoritative source files. SDK package publication is separate.", + "note": "polymorfa/polymorfa PR #388 merged into dev as 5c8014d7d58b4d3e483d9d124ee9d88bd85820f9. Both snapshots are byte-identical to the authoritative source files at that merge commit. SDK package publication is separate.", "contracts": { "messaging": { "sourcePath": "apps/api/docs/openapi.json", "snapshotPath": "contracts/openapi.messaging.json", - "sha256": "959f28d1181a085da7927b1f10240b06130de6b8a0b4a3f80fbbea9c710e0262" + "sha256": "ed0b8c863fd2400291036b2883bcedb2a409a95dfe4299f1e66d4a683fc9d2c9" }, "platform": { "sourcePath": "apps/api/docs/openapi.management.json", "snapshotPath": "contracts/openapi.platform.json", - "sha256": "17a3d97d9b8ba6b4a1e0d7d1fbc24b6be1cf579165fa02f8b7358cc6c5029892" + "sha256": "4aa6264fdaf0671246d22f61a2ece2ca70378d00893e1ce3dfc81e934c099a5d" } } } diff --git a/contracts/testing-events.json b/contracts/testing-events.json index 8be5cddc..c777e6ad 100644 --- a/contracts/testing-events.json +++ b/contracts/testing-events.json @@ -1,8 +1,8 @@ { "repository": "polymorfa/polymorfa", - "commit": "2f24c1d62", + "commit": "5c8014d7d58b4d3e483d9d124ee9d88bd85820f9", "sourcePath": "apps/api/docs/openapi.json", - "sourceSha256": "959f28d1181a085da7927b1f10240b06130de6b8a0b4a3f80fbbea9c710e0262", + "sourceSha256": "ed0b8c863fd2400291036b2883bcedb2a409a95dfe4299f1e66d4a683fc9d2c9", "schemas": { "TestEventFixtureList": { "type": "object", diff --git a/packages/typescript/src/index.ts b/packages/typescript/src/index.ts index d31a6569..8ba51bb8 100644 --- a/packages/typescript/src/index.ts +++ b/packages/typescript/src/index.ts @@ -74,6 +74,7 @@ export type { DeleteMessageOptions } from "./messaging/chats.js"; export type { HistoryChat, HistoryMedia, + HistoryMediaRetrieval, HistoryMessage, HistoryMessageSummary, HistoryPage, @@ -261,6 +262,10 @@ export type { AddCampaignRecipientsResult, Campaign, CampaignAnalytics, + CampaignSendWindow, + CampaignSendWindowDay, + CampaignSendWindowRange, + CampaignSendWindowRequest, CampaignAnalyticsResponse, CampaignOperation, CampaignOperationResponse, @@ -800,6 +805,7 @@ export type { ListCustomersParams, ListPlatformCampaignRecipientsParams, OptOutSettings, + PlatformCampaign, PlatformCampaignParams, ReschedulePlatformCampaignRequest, PlatformCampaignRecipientsEnvelope, diff --git a/packages/typescript/src/messaging/history.ts b/packages/typescript/src/messaging/history.ts index 4f67bc5e..77dd91e8 100644 --- a/packages/typescript/src/messaging/history.ts +++ b/packages/typescript/src/messaging/history.ts @@ -26,6 +26,20 @@ export interface HistoryMedia { readonly url: string; } +/** Official API media storage status on a hosted history message. */ +export interface HistoryMediaRetrieval { + readonly state: + | "pending" + | "stored" + | "unavailable" + | "expired" + | "too_large" + | "unsupported_type" + | "failed" + | "cancelled"; + readonly reason?: string; +} + export interface HistoryMessage extends HistoryMessageSummary { readonly conversation: ConversationIdentity & { readonly sender?: ConversationIdentity; @@ -51,6 +65,7 @@ export interface HistoryMessage extends HistoryMessageSummary { readonly hash: string; }[]; readonly media?: readonly HistoryMedia[]; + readonly mediaRetrieval?: HistoryMediaRetrieval; } export interface HistoryPage { diff --git a/packages/typescript/src/messaging/quicklinks.ts b/packages/typescript/src/messaging/quicklinks.ts index 741139b0..75502b7d 100644 --- a/packages/typescript/src/messaging/quicklinks.ts +++ b/packages/typescript/src/messaging/quicklinks.ts @@ -27,7 +27,7 @@ export interface QuickLinkConfiguration extends SessionConfigurationOverrides { }; } -export type QuickLinkPurpose = "initial" | "add_connection"; +export type QuickLinkPurpose = "initial" | "add_connection" | "reauthorization"; export type QuickLinkConnectionGoal = "single" | "hybrid"; export type QuickLinkConnectionKind = "linked_devices" | "official_api"; export type QuickLinkHybridPhase = diff --git a/packages/typescript/src/messaging/types.ts b/packages/typescript/src/messaging/types.ts index 13d0ca96..c5eba77a 100644 --- a/packages/typescript/src/messaging/types.ts +++ b/packages/typescript/src/messaging/types.ts @@ -103,6 +103,8 @@ export interface Campaign { readonly completedAt: number | null; readonly createdAt: number; readonly updatedAt: number; + /** Local hours the campaign may send in, or null when sending is unrestricted. */ + readonly sendWindow: CampaignSendWindow | null; /** Additional live repository fields omitted from the pinned OpenAPI schema. */ readonly composerBlueprint?: unknown; readonly messages?: unknown; @@ -113,6 +115,51 @@ export interface Campaign { readonly variantStrategy?: unknown; } +export type CampaignSendWindowDay = + | "monday" + | "tuesday" + | "wednesday" + | "thursday" + | "friday" + | "saturday" + | "sunday"; + +/** Local `HH:MM` range; `start` is inclusive and `end` is exclusive. */ +export interface CampaignSendWindowRange { + readonly start: string; + /** Later than `start`; `24:00` ends the range at midnight. */ + readonly end: string; +} + +/** + * When a campaign may send. Recipients outside the window stay `queued` and + * are sent when it next opens. + */ +export interface CampaignSendWindowRequest { + /** + * IANA time zone the window is written in. When omitted, the team's time + * zone is stored, or `UTC` when the team has none. + */ + readonly timeZone?: string; + /** One to seven distinct weekdays, in the window's time zone. */ + readonly days: readonly CampaignSendWindowDay[]; + /** One to four non-overlapping ranges; a range cannot cross midnight. */ + readonly hours: readonly CampaignSendWindowRange[]; + /** Evaluate the window in each recipient's own time zone. Defaults to false. */ + readonly recipientTimeZone?: boolean; + /** Recipient variable holding an IANA time zone. Defaults to `timeZone`. */ + readonly timeZoneVariable?: string; +} + +/** Stored send window returned on a campaign. */ +export interface CampaignSendWindow { + readonly timeZone: string; + readonly days: readonly CampaignSendWindowDay[]; + readonly hours: readonly CampaignSendWindowRange[]; + readonly recipientTimeZone: boolean; + readonly timeZoneVariable: string; +} + export interface CampaignAnalytics { readonly campaignId: string; readonly recipientCount: number; @@ -137,6 +184,8 @@ export interface CreateCampaignRequest { * whole request; use `campaigns.addRecipients` for partial acceptance. */ readonly recipients?: readonly CampaignRecipientInput[]; + /** Local hours the campaign may send in. Omit it to send at any hour. */ + readonly sendWindow?: CampaignSendWindowRequest; } export type CampaignRecipientStatus = @@ -230,6 +279,11 @@ interface UpdateCampaignChanges { readonly senderConfig?: Readonly>; /** Epoch milliseconds, or null to start at launch. */ readonly scheduledAt?: number | null; + /** + * Null removes the window. Only a draft or paused campaign can change it; a + * paused edit requires in-flight sends to have settled. + */ + readonly sendWindow?: CampaignSendWindowRequest | null; } /** At least one draft field must be supplied. */ @@ -239,7 +293,10 @@ export type UpdateCampaignRequest = | (UpdateCampaignChanges & { readonly senderConfig: Readonly>; }) - | (UpdateCampaignChanges & { readonly scheduledAt: number | null }); + | (UpdateCampaignChanges & { readonly scheduledAt: number | null }) + | (UpdateCampaignChanges & { + readonly sendWindow: CampaignSendWindowRequest | null; + }); export interface RescheduleCampaignRequest { /** Epoch milliseconds, or null to start a waiting campaign now. */ @@ -1737,6 +1794,7 @@ export interface VoipAddParticipantRequest { export type VoipParticipantState = "invited" | "ringing" | "connected" | "left"; export interface VoipParticipant { + readonly handRaised?: boolean; readonly id: string; readonly phoneNumber?: string; readonly bsuid?: string; diff --git a/packages/typescript/src/platform/campaigns.ts b/packages/typescript/src/platform/campaigns.ts index 3b4b9eb5..aa54a958 100644 --- a/packages/typescript/src/platform/campaigns.ts +++ b/packages/typescript/src/platform/campaigns.ts @@ -11,6 +11,7 @@ import type { DataEnvelope, ListCampaignsParams, ListPlatformCampaignRecipientsParams, + PlatformCampaign, PlatformCampaignParams, PlatformCampaignRecipientsEnvelope, PlatformPayload, @@ -19,6 +20,7 @@ import type { } from "./types.js"; type CampaignResponse = Promise>>; +type CampaignResult = Promise>>; type CreateCampaignOptions = Omit< RequestOptions, "idempotencyKey" | "maxNetworkRetries" @@ -33,7 +35,7 @@ export class CampaignsResource { list( params: ListCampaignsParams, options: RequestOptions = {}, - ): CampaignResponse { + ): CampaignResult { return this.transport.request({ method: "GET", path: "/platform/campaigns", @@ -50,7 +52,7 @@ export class CampaignsResource { create( body: CreatePlatformCampaignRequest, options: CreateCampaignOptions = {}, - ): CampaignResponse { + ): CampaignResult { return this.transport.request({ method: "POST", path: "/platform/campaigns", @@ -71,7 +73,7 @@ export class CampaignsResource { campaignId: string, params: PlatformCampaignParams, options: RequestOptions = {}, - ): CampaignResponse { + ): CampaignResult { return this.transport.request({ method: "GET", path: campaignPath(campaignId), @@ -90,7 +92,7 @@ export class CampaignsResource { body: UpdatePlatformCampaignRequest | undefined, params: PlatformCampaignParams, options: RequestOptions = {}, - ): CampaignResponse { + ): CampaignResult { return this.transport.request({ method: "PATCH", path: campaignPath(campaignId), diff --git a/packages/typescript/src/platform/types.ts b/packages/typescript/src/platform/types.ts index 8f7546af..dba38957 100644 --- a/packages/typescript/src/platform/types.ts +++ b/packages/typescript/src/platform/types.ts @@ -1,7 +1,9 @@ import type { + Campaign, CampaignRecipient, CampaignRecipientInput, CampaignRecipientStatus, + CampaignSendWindowRequest, InvalidRecipientRow, } from "../messaging/types.js"; @@ -187,6 +189,8 @@ export interface CreatePlatformCampaignRequest { readonly recipients?: readonly CampaignRecipientInput[]; /** Ignored when inline recipients are supplied. */ readonly recipientCount?: number; + /** Local hours the campaign may send in; null or omitted sends at any hour. */ + readonly sendWindow?: CampaignSendWindowRequest | null; // The API deliberately leaves these JSON values opaque. readonly composerBlueprint?: unknown; readonly messagesArray?: unknown; @@ -196,6 +200,14 @@ export interface CreatePlatformCampaignRequest { readonly variantStrategy?: unknown; } +/** + * Campaign returned by Platform list, get, create and update. The contract + * keeps the object open, so unnamed fields pass through as `unknown`. + */ +export interface PlatformCampaign extends Campaign { + readonly [field: string]: unknown; +} + export interface ListCampaignsParams { readonly projectId: string; readonly projectSlug?: string; @@ -226,7 +238,14 @@ export interface ReschedulePlatformCampaignRequest extends PlatformCampaignParam * not been copied into recipients. After that the API refuses the change. */ export interface UpdatePlatformCampaignRequest { + /** 1 to 200 characters. */ + readonly name?: string; readonly recipientListId?: string | null; + readonly senderConfig?: Readonly>; + /** Unix milliseconds, or null. Only while the campaign is an unlaunched draft. */ + readonly scheduledAt?: number | null; + /** Null removes the window. Only while the campaign is a draft or paused. */ + readonly sendWindow?: CampaignSendWindowRequest | null; readonly [field: string]: unknown; } diff --git a/packages/typescript/test/campaigns.test.ts b/packages/typescript/test/campaigns.test.ts index 167deee0..ef40e5c3 100644 --- a/packages/typescript/test/campaigns.test.ts +++ b/packages/typescript/test/campaigns.test.ts @@ -11,6 +11,9 @@ import { type CampaignOperationResponse, type CampaignRecipient, type CampaignRequeueResponse, + type CampaignSendWindow, + type CampaignSendWindowRequest, + type PlatformCampaign, type CampaignStopResponse, type CreateCampaignResponse, type GetCampaignResponse, @@ -48,6 +51,7 @@ const campaign = { completedAt: null, createdAt: 1_724_000_000_000, updatedAt: 1_724_000_000_000, + sendWindow: null, }; const recipient = { @@ -150,6 +154,31 @@ describe("MessagingClient campaigns", () => { expectTypeOf().toHaveProperty("recipientCount"); expectTypeOf().toHaveProperty("scheduledAt"); expectTypeOf().toHaveProperty("responseRate"); + expectTypeOf< + Campaign["sendWindow"] + >().toEqualTypeOf(); + expectTypeOf< + PlatformCampaign["sendWindow"] + >().toEqualTypeOf(); + }); + + it("sends a send window update and removes it with null", async () => { + const { client, requests } = await campaignsServer(); + const window: CampaignSendWindowRequest = { + timeZone: "America/Sao_Paulo", + days: ["monday", "tuesday"], + hours: [{ start: "09:00", end: "18:00" }], + }; + await client.campaigns.update("launch/eu", campaign.id, { + sendWindow: window, + }); + await client.campaigns.update("launch/eu", campaign.id, { + sendWindow: null, + }); + expect(requests.map(({ body }) => JSON.parse(body))).toEqual([ + { sendWindow: window }, + { sendWindow: null }, + ]); }); it("maps complete project reads without inventing pagination", async () => { diff --git a/packages/typescript/test/coverage-reconciliation.test.ts b/packages/typescript/test/coverage-reconciliation.test.ts index 59b16e25..384fd14b 100644 --- a/packages/typescript/test/coverage-reconciliation.test.ts +++ b/packages/typescript/test/coverage-reconciliation.test.ts @@ -196,7 +196,7 @@ describe("reconciled coverage evidence", () => { }; expect(source.repository).toBe("polymorfa/polymorfa"); // Repinning the reviewed source requires updating this regression gate too. - expect(source.commit).toBe("2f24c1d62"); + expect(source.commit).toBe("5c8014d7d58b4d3e483d9d124ee9d88bd85820f9"); expect(ledger.sourceCommit).toBe(source.commit); expect(Object.keys(source.contracts).sort()).toEqual([ "messaging", @@ -233,8 +233,9 @@ describe("reconciled coverage evidence", () => { "proves $operationId uses its covered HTTP route and body", async (fixture) => { const mapping = entry(fixture.operationId); - expect(mapping.typescript).toEqual({ - status: "covered", + expect(mapping.typescript).toMatchObject({ + // Group placement is implemented on SDK dev; `to` placement is typed here. + status: fixture.operationId === "voipPlaceCall" ? "partial" : "covered", method: `MessagingClient.voip.${fixture.method}`, }); const fetch = vi.fn(async () => diff --git a/packages/typescript/test/coverage.test.ts b/packages/typescript/test/coverage.test.ts index 5fc5c315..360922ea 100644 --- a/packages/typescript/test/coverage.test.ts +++ b/packages/typescript/test/coverage.test.ts @@ -173,12 +173,12 @@ describe("coverage checker", () => { const result = runRepositoryChecker(); expect(result.status, result.stderr).toBe(0); expect(result.report).toMatchObject({ - sourceCommit: "2f24c1d62", - total: 512, - covered: 382, - partial: 0, - missing: 0, - excluded: 130, + sourceCommit: "5c8014d7d58b4d3e483d9d124ee9d88bd85820f9", + total: 529, + covered: 381, + partial: 1, + missing: 15, + excluded: 132, changed: 0, }); const resolutions = (result.report?.resolutions ?? []) as Array<{ @@ -235,6 +235,12 @@ describe("coverage checker", () => { ); expect(mappings).toEqual({ exportCallRecords: { status: "covered", method: "Client.calls.export" }, + getCallRecord: { + status: "missing", + reason: + "Client.calls.retrieve: Implemented on SDK dev; not on this main-based branch until dev merges.", + milestone: "sdk-dev-calls", + }, getCallStats: { status: "covered", method: "Client.calls.stats" }, listCallRecords: { status: "covered", method: "Client.calls.list" }, }); diff --git a/packages/typescript/test/platform-campaign-create.test.ts b/packages/typescript/test/platform-campaign-create.test.ts index f49cd867..b04c8541 100644 --- a/packages/typescript/test/platform-campaign-create.test.ts +++ b/packages/typescript/test/platform-campaign-create.test.ts @@ -32,6 +32,13 @@ const body = { complianceConfig: false, variants: ["a", "b"], variantStrategy: "round_robin", + sendWindow: { + timeZone: "America/Sao_Paulo", + days: ["monday", "friday"], + hours: [{ start: "09:00", end: "18:00" }], + recipientTimeZone: true, + timeZoneVariable: "timeZone", + }, } satisfies CreatePlatformCampaignRequest; it("covers exactly the pinned create request fields without closing opaque JSON", () => { From d463581ce3aba30b9729a98a7e83d92ffef31a93 Mon Sep 17 00:00:00 2001 From: Rajeh Taher Date: Sun, 27 Sep 2026 19:37:41 +0200 Subject: [PATCH 8/9] feat(webhooks): type reply choice and quoted message fields on message.received (cherry picked from commit 2f87772a1e6002846e5c5cc37d893711e1d48d7b) --- CHANGELOG.md | 4 ++++ packages/typescript/src/index.ts | 1 + packages/typescript/src/webhooks/events.ts | 16 ++++++++++++++++ packages/typescript/src/webhooks/index.ts | 1 + .../typescript/test/webhook-event-types.test.ts | 12 ++++++++++++ 5 files changed, 34 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 98ddc093..6ed69c1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## Unreleased +- `message.received` payload types include `replyChoice` (`kind` `button` or + `list` and the `id` you assigned) and `parentMessageId`, and the linked-device + message `type` union adds `button_reply`, `list_reply`, and + `native_flow_response`. `ReplyChoice` is exported. - Added typed hosted message history reads to `MessagingClient.chats`: `list`, `retrieve`, `listMessages`, and `retrieveMessage`. Server credentials need the relevant read scope, HMS enabled on the Number, and enrollment in diff --git a/packages/typescript/src/index.ts b/packages/typescript/src/index.ts index 8ba51bb8..5d775a42 100644 --- a/packages/typescript/src/index.ts +++ b/packages/typescript/src/index.ts @@ -1012,6 +1012,7 @@ export { type MessageReceivedPayload, type MessageSentPayload, type NativeFlowResponse, + type ReplyChoice, type NewsletterUpdatePayload, type PollOption, type PollVotePayload, diff --git a/packages/typescript/src/webhooks/events.ts b/packages/typescript/src/webhooks/events.ts index 4d6de017..fb69974c 100644 --- a/packages/typescript/src/webhooks/events.ts +++ b/packages/typescript/src/webhooks/events.ts @@ -123,6 +123,15 @@ export interface NativeFlowResponse { readonly version?: number; } +/** + * The reply button or list row a contact chose. `id` is the ID you assigned + * when sending; the visible label is not included. + */ +export interface ReplyChoice { + readonly kind: "button" | "list"; + readonly id: string; +} + export interface PollOption { readonly name: string; readonly hash: string; @@ -137,6 +146,9 @@ export type LinkedDeviceMessageType = | "location" | "contact" | "phone_number_shared" + | "native_flow_response" + | "button_reply" + | "list_reply" | "poll" | "sticker" | "reaction" @@ -174,6 +186,8 @@ export interface LinkedDeviceMessagePayload { readonly unavailable?: boolean; readonly unavailableReason?: string; readonly nativeFlowResponse?: NativeFlowResponse; + readonly replyChoice?: ReplyChoice; + readonly parentMessageId?: string; readonly [key: string]: unknown; } @@ -198,6 +212,8 @@ export interface CloudMessagePayload { readonly type: string; readonly senderName?: string; readonly nativeFlowResponse?: NativeFlowResponse; + readonly replyChoice?: ReplyChoice; + readonly parentMessageId?: string; readonly interactive?: Readonly>; readonly referral?: CloudMessageReferral; readonly [key: string]: unknown; diff --git a/packages/typescript/src/webhooks/index.ts b/packages/typescript/src/webhooks/index.ts index cce85a83..35250abd 100644 --- a/packages/typescript/src/webhooks/index.ts +++ b/packages/typescript/src/webhooks/index.ts @@ -90,6 +90,7 @@ export { type MessageReceivedPayload, type MessageSentPayload, type NativeFlowResponse, + type ReplyChoice, type NewsletterUpdatePayload, type PollOption, type PollVotePayload, diff --git a/packages/typescript/test/webhook-event-types.test.ts b/packages/typescript/test/webhook-event-types.test.ts index f8bca75b..f769f5bb 100644 --- a/packages/typescript/test/webhook-event-types.test.ts +++ b/packages/typescript/test/webhook-event-types.test.ts @@ -38,6 +38,7 @@ import type { MessagePayload, MessageReceivedPayload, NativeFlowResponse, + ReplyChoice, NewsletterUpdatePayload, PollOption, PollVotePayload, @@ -77,6 +78,11 @@ type ExpectedPollOption = { readonly hash: string; }; +type ExpectedReplyChoice = { + readonly kind: "button" | "list"; + readonly id: string; +}; + type ExpectedLinkedDeviceMessageType = | "text" | "image" @@ -86,6 +92,9 @@ type ExpectedLinkedDeviceMessageType = | "location" | "contact" | "phone_number_shared" + | "native_flow_response" + | "button_reply" + | "list_reply" | "poll" | "sticker" | "reaction" @@ -122,6 +131,8 @@ type ExpectedMessagePayload = { readonly unavailable?: boolean; readonly unavailableReason?: string; readonly nativeFlowResponse?: ExpectedNativeFlowResponse; + readonly replyChoice?: ExpectedReplyChoice; + readonly parentMessageId?: string; readonly [key: string]: unknown; }; @@ -461,6 +472,7 @@ describe("webhook event payload types", () => { expectTypeOf().toEqualTypeOf(); expectTypeOf().toEqualTypeOf(); expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf(); expectTypeOf().toEqualTypeOf(); expectTypeOf().toEqualTypeOf(); expectTypeOf().toEqualTypeOf< From 2574141e30a5feaac6bed6a56527289395fa09d5 Mon Sep 17 00:00:00 2001 From: Rajeh Taher Date: Thu, 1 Oct 2026 00:43:38 +0300 Subject: [PATCH 9/9] Type merged webhook payload additions and document the repin Cherry-picked reply choice types from SDK dev; add message.ack pricing and webhook participant handRaised; record the reviewed ledger changes. --- CHANGELOG.md | 9 +++ contracts/README.md | 64 +++++++++++++++---- packages/typescript/src/index.ts | 1 + packages/typescript/src/webhooks/events.ts | 19 ++++++ packages/typescript/src/webhooks/index.ts | 1 + .../test/webhook-event-types.test.ts | 13 ++++ 6 files changed, 96 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ed69c1a..0035c72b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ ## Unreleased +- Campaign types include `sendWindow`. `Campaign.sendWindow` is a + `CampaignSendWindow` or null, and campaign create and update requests on + both clients accept a `CampaignSendWindowRequest`; update accepts null to + remove the window. `Client.campaigns.list`, `retrieve`, `create` and + `update` return `PlatformCampaign` instead of an untyped payload. +- `QuickLinkPurpose` includes `reauthorization`. Hosted history messages + expose `mediaRetrieval`, call participants expose optional `handRaised`, and + `message.ack` payloads expose the optional Official API `pricing` + classification (`MetaPricingReport`). - `message.received` payload types include `replyChoice` (`kind` `button` or `list` and the `id` you assigned) and `parentMessageId`, and the linked-device message `type` union adds `button_reply`, `list_reply`, and diff --git a/contracts/README.md b/contracts/README.md index b2686f91..3751e259 100644 --- a/contracts/README.md +++ b/contracts/README.md @@ -42,13 +42,55 @@ published SDK package before updating their pinned dependency. ## Full snapshots The snapshots are byte-identical copies of the Messaging and Platform OpenAPI -files at `polymorfa/polymorfa` PR #388 source commit `2f24c1d62` (coordinated -until that PR merges). The response to feature evaluation now includes a nullable +files at `polymorfa/polymorfa` commit +`5c8014d7d58b4d3e483d9d124ee9d88bd85820f9`, the merge of PR #388 into API +`dev`. `testing-events.json` uses the same commit; its four schemas are +unchanged. The response to feature evaluation includes a nullable `experimentRevision`; both evaluation routes remain excluded from SDK methods -because they require a host runtime or live dashboard identity. The full refresh -also reconciles the Campaigns update and reschedule routes. `source.json` records -the exact source paths and SHA-256 hashes. SDK package publication remains -separate. +because they require a host runtime or live dashboard identity. The Platform +referral route was removed on API `dev` before this merge, so the snapshot has +no `/platform/referral` operation; referrals stay Console-only. `source.json` +records the exact source paths and SHA-256 hashes. SDK package publication +remains separate. + +Moving from PR source commit `2f24c1d62` to the merge commit adds 17 +operations and changes 236 fingerprints; no operation was removed. The +fingerprint changes were reviewed: + +- 211 operations only add documented `X-RateLimit-Limit`, + `X-RateLimit-Remaining` and `X-RateLimit-Reset` response headers. Most of + the other changed operations add the same headers. +- Campaigns add `sendWindow` on both surfaces. `Campaign.sendWindow` is + `CampaignSendWindow | null`; Messaging create and update, and Platform create + and update, accept `CampaignSendWindowRequest` (update and Platform create + accept null to remove it). Platform list, get, create and update now declare + `PlatformCampaign`, an open object with the Messaging campaign fields, so + those methods return it instead of `PlatformPayload`. The Platform update + body names `name`, `senderConfig`, `scheduledAt` and `sendWindow` and stays + open. +- QuickLink create, get and status add the `reauthorization` purpose to + `QuickLinkPurpose`. The public QuickLink token routes stay excluded. +- Hosted-history message reads add `mediaRetrieval` (`HistoryMediaRetrieval`). +- `VoipParticipant` adds optional `handRaised`. +- `POST /messaging/voip/calls` now accepts exactly one of `to`, + `participants` or `groupId`. `MessagingClient.voip.place` types only `to` + here, so the row is partial; group placement is implemented on SDK `dev`. +- The two account feature routes add a body `projectId`; they stay excluded. + +The 17 added operations are recorded as follows. Two Console account-health +routes (`/api/account/cloud-health` and its export) are excluded because they +require dashboard identity. Six Calls operations (call links, link preview, +raised hand, ring participant, call reaction and `GET /platform/calls/{callId}`) +have typed methods on SDK `dev` and are missing on this branch until `dev` +merges. Nine Official API operations are missing: four Cloud template routes +(proposed in SDK PR #308), stored message media download (proposed in SDK PR +#316), the customer service window, credential health, credential +reauthorization and the Meta pricing summary. + +Webhook payload types follow the same commit: `message.received` adds +`replyChoice` and `parentMessageId` and three linked-device message types, +`message.ack` adds optional `pricing` (`MetaPricingReport`), and call +participants add optional `handRaised`. `MessagingClient.campaigns.update` covers the new draft update route, while `MessagingClient.campaigns.reschedule` and `Client.campaigns.reschedule` cover @@ -167,12 +209,12 @@ retired both events and `bansafe.enforcement`. | Status | Operations | | ------------------- | ---------: | -| Covered | 382 | -| Missing | 0 | -| Excluded | 130 | -| Partial | 0 | +| Covered | 381 | +| Missing | 15 | +| Excluded | 132 | +| Partial | 1 | | Changed fingerprint | 0 | -| Total | 512 | +| Total | 529 | This revision adds test event triggering (`POST /messaging/testing/{projectId}/events`) and fixture listing diff --git a/packages/typescript/src/index.ts b/packages/typescript/src/index.ts index 5d775a42..af4fc91d 100644 --- a/packages/typescript/src/index.ts +++ b/packages/typescript/src/index.ts @@ -1013,6 +1013,7 @@ export { type MessageSentPayload, type NativeFlowResponse, type ReplyChoice, + type MetaPricingReport, type NewsletterUpdatePayload, type PollOption, type PollVotePayload, diff --git a/packages/typescript/src/webhooks/events.ts b/packages/typescript/src/webhooks/events.ts index fb69974c..97e816a3 100644 --- a/packages/typescript/src/webhooks/events.ts +++ b/packages/typescript/src/webhooks/events.ts @@ -232,6 +232,22 @@ export interface MessageSentPayload { readonly timestamp: number; } +/** + * Official API only. The pricing classification WhatsApp reported for a + * message, copied as reported. Meta bills the WhatsApp Business Account + * directly; this is not a Polymorfa charge and contains no price. + */ +export interface MetaPricingReport { + /** Deprecated by WhatsApp; use `type`. */ + readonly billable?: boolean; + /** For example `PMP` (per-message pricing). */ + readonly pricing_model?: string; + /** For example `marketing`, `utility`, `authentication` or `service`. */ + readonly category?: string; + /** For example `regular`, `free_customer_service` or `free_entry_point`. */ + readonly type?: string; +} + export interface MessageAckPayload { readonly messages: readonly { readonly id: string; @@ -244,6 +260,7 @@ export interface MessageAckPayload { readonly sender?: IdentityReference; readonly type: string; readonly timestamp: number; + readonly pricing?: MetaPricingReport; } export interface MessageDeletePayload { @@ -460,6 +477,8 @@ export interface CallTelemetryPayload { } export interface CallParticipant { + /** Authoritative raised-hand state for a connected participant. */ + readonly handRaised?: boolean; readonly id: string; readonly phoneNumber?: string; readonly bsuid?: string; diff --git a/packages/typescript/src/webhooks/index.ts b/packages/typescript/src/webhooks/index.ts index 35250abd..6f337e64 100644 --- a/packages/typescript/src/webhooks/index.ts +++ b/packages/typescript/src/webhooks/index.ts @@ -91,6 +91,7 @@ export { type MessageSentPayload, type NativeFlowResponse, type ReplyChoice, + type MetaPricingReport, type NewsletterUpdatePayload, type PollOption, type PollVotePayload, diff --git a/packages/typescript/test/webhook-event-types.test.ts b/packages/typescript/test/webhook-event-types.test.ts index f769f5bb..94b3f24a 100644 --- a/packages/typescript/test/webhook-event-types.test.ts +++ b/packages/typescript/test/webhook-event-types.test.ts @@ -33,7 +33,9 @@ import type { WebhookConversationReference, LabelsUpdatePayload, LinkedDeviceMessageType, + MessageAckPayload, MessageDeletePayload, + MetaPricingReport, MessageEchoPayload, MessagePayload, MessageReceivedPayload, @@ -187,6 +189,7 @@ type ExpectedPayloads = { readonly phoneNumber?: string; readonly bsuid?: string; readonly username?: string; + readonly handRaised?: boolean; readonly audioMuted: false; readonly video: false; readonly state: "invited" | "ringing" | "connected" | "left"; @@ -204,6 +207,7 @@ type ExpectedPayloads = { readonly phoneNumber?: string; readonly bsuid?: string; readonly username?: string; + readonly handRaised?: boolean; readonly audioMuted: false; readonly video: false; readonly state: "invited" | "ringing" | "connected" | "left"; @@ -473,6 +477,15 @@ describe("webhook event payload types", () => { expectTypeOf().toEqualTypeOf(); expectTypeOf().toEqualTypeOf(); expectTypeOf().toEqualTypeOf(); + expectTypeOf().toEqualTypeOf< + MetaPricingReport | undefined + >(); + expectTypeOf().toEqualTypeOf<{ + readonly billable?: boolean; + readonly pricing_model?: string; + readonly category?: string; + readonly type?: string; + }>(); expectTypeOf().toEqualTypeOf(); expectTypeOf().toEqualTypeOf(); expectTypeOf().toEqualTypeOf<