- Read permission allows GET only. Write permission adds POST and DELETE. A read-only key on a write route returns a
403that the plugin reports asisPermissionError, which is a different problem from a401(a wrong or deleted key) and needs a new key rather than a retry.
survey.sendsends real email. It queues a survey through a transactional campaign and returns onlystatus: 'queued'; there is no survey ID to cancel. A non-transactional campaign is a400, an inactive one a403, and an unknown one a404.delayoverrides the campaign’s own scheduling, so omit it to respect the campaign’s sending time frame.feedback.importchanges your scores. Imported responses count toward NPS and trends, bucketed byresponse_date, so set it on a historical import or every response lands on the import day. They do not consume send quota and never fire webhooks. Only 200 responses go in one call, and a bad item fails alone: readdetails.failedfor the reason instead of treating a200as all-or-nothing.- The main comment and additional questions collide. A top-level
commentfills the template’sisMainquestion, so also answering that question inadditional_questionsfails the item as ambiguous. Get question UUIDs fromtemplates.get. - RATING answers need their metric. An
additional_questionsentry of typeRATINGis scored on a metric (nps,csat,ces,star,thumbs,xsatorpmf); the plugin rejects one that omits it before the request is sent. - Dates are not uniform.
customers.list,feedback.listandoutbox.listacceptYYYY-MM-DD, an ISO 8601 date-time, or UNIX seconds - milliseconds silently resolve to a far-future date.reports.getis the exception: it takesYYYY-MM-DDonly and rejects a full timestamp.feedback.importdates are ISO 8601 only. - Paging stops at 1,000.
limitis capped there, anddata.pagestells you how many pages exist, so walk them rather than asking for everything at once. - Unsubscribing and resubscribing are not symmetric.
customers.resubscribeis idempotent and reports a status per contact: onlyresubscribedmeans a change,skippedmeans Retently refused for compliance (self-unsubscribed or hard-bounced), andsuppressed: truewarns that an account-level suppression still blocks delivery. - Rate limit. 150 requests a minute. No
Retry-Afteris documented, so the plugin backs off exponentially on a429.
- 18 typed API operations
Setup
1
Install
2
Add the plugin
corsair.ts
multiTenancy: true, scope calls with corsair.withTenant(id). See Quick start for KEK + Hub keys, and Multi-tenancy for account isolation.3
Choose authentication
- API Key (Recommended)
No setup required yet. When you make your first request as a tenant, Corsair prompts for the API key.More: API Key
4
Connect a tenant
Mint a connect link and send the tenant to it. Hub hosts the page and delivers the result to your app — see Connect / OAuth.
Example API calls
Read the latest NPS responsesWhat’s next
API reference
Every
retently.api.* operation with input and output types.Connect / OAuth
createLink, Hub delivery, and tenant connect flows.
Use with agents
Expose this plugin’s operations as MCP tools.