Skip to content
Esc
  • OverviewGuidesWhat exists today, and where to start.
  • QuickstartGuidesKey, domain, first send — in that order.
  • AuthenticationGuidesBearer keys, the mandatory User-Agent, and what each refusal means.
  • ErrorsGuidesThe whole vocabulary, with the status each name carries.
  • IdempotencyGuidesRetry a send without sending it twice.
  • PaginationGuidesCursors are item IDs, not page numbers.
  • Rate limitsGuidesTen a second per team, and the headers that tell you where you are.
  • EventsGuidesEvery event a webhook can carry, with one real payload each.
  • DomainsGuidesThe records, where they go at each registrar, and what the page does while you wait.
  • TrackingGuidesOpens and clicks: one record, two toggles, and what an open really means.
  • ReceivingGuidesInbound mail, and the Inbox: a webhook fires, you read it, you answer it.
  • InboxGuidesChannels, personal mailboxes and seats: who sees what, and where a reply goes.
  • Node SDKGuidesThe rasket package: typed from the API's own document, retries only what is safe.
  • Python SDKGuidesThe rasket package on PyPI: the Node client's methods, in snake_case, over httpx.
  • MCP serverGuidesConnect Claude, ChatGPT or any MCP client: your scopes, no key.
  • AI assistGuidesSubject lines, drafts and diagnosis — in the dashboard and over the API, off until you allow it.
  • AgentsGuidesLet an AI agent set Rasket up: the skill, the rules file, MCP, and the recipe they share.
  • OAuthGuidesLet another app act for a team: register, authorize with PKCE, exchange, refresh.
  • Single sign-onGuidesOIDC login for your team, a domain proved by DNS, enforcement and break-glass.
  • IntegrationsGuidesVercel, Netlify and Cloudflare, plus Zapier and n8n for workflows without code.
  • SMTPGuidesSend from anything that speaks SMTP: settings, setup guides, limits and replies.
  • ZapierGuidesSend email, add contacts and react to email events from a Zap, with no code.
  • n8nGuidesThe Rasket node and trigger for n8n workflows: install, connect, every operation.
  • EmailsAPI referenceSend, batch, retrieve, list, reschedule, cancel, attachments.
  • DomainsAPI referenceAdd a domain, publish its records, verify it.
  • API keysAPI referenceCreate, list, rename and revoke credentials.
  • WebhooksAPI referencePayloads, signature verification, retries and replay.
  • SuppressionsAPI referenceAddresses we will not send to, and why.
  • LogsAPI referenceEvery request made with this team's credentials.
  • MetricsAPI referenceDelivery, bounce, complaint and engagement counts.
  • TemplatesAPI referenceVersioned email content with typed variables, addressed by ID or alias.
  • ContactsAPI referenceYour audience: contacts, their typed properties, segments and topic choices.
  • SegmentsAPI referenceAudiences defined by a filter, by hand, or both.
  • TopicsAPI referenceWhat contacts subscribe to, and the preference page's list.
  • CampaignsAPI referenceCampaigns, at /broadcasts: one message to a segment, from draft to results.
  • ImportsAPI referenceCSV uploads: column mapping, conflicts and counts.
  • AutomationsAPI referenceWorkflows that run per contact: the graph, its versions, and every run.
  • Custom eventsAPI referenceThe names your product fires, and what starts a workflow.
  • ReceivingAPI referenceMail sent to you: the message, its attachments, its raw source.
  • OAuthAPI referenceClient registration, the token endpoint, and the grants a team has given.
  • TeamAPI referenceThe team a credential belongs to: its plan, sender identity, AI flag and members.
  • BillingAPI referencePlan, usage, invoices and add-ons, and the hosted pages where a customer pays.
  • AI helpersAPI referenceSubject lines, a first draft, and why an email did what it did.

API referenceTemplates

Templates

Reusable email content with typed variables. Write it once, publish it, and send it by ID or alias with the values filled in.

Versions

A template is a name, an optional alias, and a series of versions of its content — from, subject, reply_to, html, text and the variables they use. Creating a template writes version 1 as a draft. publish makes the current version the one sends use; updating a published template opens the next draft without changing what is sent, until you publish again.

  • Retrieving a template shows the current version. When that is an open draft, has_unpublished_versions is true.
  • A send records the exact version it used, so a later edit never changes what an already-sent email said.
  • Only a published template can be sent. Naming a draft in POST /emails is 422 invalid_parameter.
  • A send may leave out from and subject when the published version sets them. Values in the request win. If neither has one, the send is 422 missing_required_field.

Variables and placeholders

A placeholder is {{key}} — or {{ key }} — with the key matching ^[A-Za-z0-9_]+$. There is no logic, no filter and no nesting: a value that contains {{other}} is inserted as that literal text, never expanded. Every placeholder the bodies use must be declared in variables, and every declared variable must be used, so an editor can point at exactly what is wrong.

Sends supply values as strings or numbers. A value is checked against the declared type; a key the send omits takes the variable's fallback_value, and a declared key with neither is 422 missing_required_field.

How each variable type renders
TypeRendered as
stringInserted as written: HTML-escaped in html, raw in subject and text.
numberWritten out as a number.
booleantrue or false.
objectCompact JSON, HTML-escaped in html.
listCompact JSON, HTML-escaped in html.

Addressing a template

  • Every {id} below accepts the template's UUID or its alias. An alias is lower-case, may not be a UUID, and is unique among the team's live templates.
  • Reading and listing work with either kind of key. Creating, updating, publishing, duplicating and deleting need a full_access key; a sending_access key gets 401 restricted_api_key.
  • A template in another team is 404 not_found, indistinguishable from one that does not exist.

Endpoints

Create a template

POST /templates

A new draft, holding version 1 of its content.

Body

  • namestringRequired

    A name for the dashboard.

  • aliasstring

    A short name to address the template by instead of its ID: lower-case letters, digits, - and _, starting with a letter or digit, at most 63 characters, and never a UUID. Unique among the team's templates.

  • fromstring

    The sender, as Name <address@domain> or a bare address. A send that names its own from overrides it.

  • subjectstring

    The subject line. May carry {{key}} placeholders.

  • htmlstring

    The HTML body. May carry {{key}} placeholders. On create, send this or starter — exactly one.

  • textstring

    The plain-text body. May carry {{key}} placeholders.

  • variablesobject[]

    The variables the bodies use, at most 200: { key, type, fallback_value }, with type one of string, number, boolean, object or list. Every {{key}} must be declared here, and every declared key must be used.

  • starterstring

    The slug of a starter template to copy instead of writing html. Its subject, layout and variables become version 1: the copy opens in the dashboard's visual editor, and its HTML and plain text are compiled from the layout. Send this or html, never both.

2 more fields (reply_to, brand_overrides)
  • reply_tostring[]

    Reply-to addresses.

  • brand_overridesobject | null

    This template's own values for your brand fields, for a one-off that should not wear the project brand: logo_url, primary_color, accent_color, font, button_style, company_name, address, footer_text. A field you leave out follows the project brand; a field set to null uses our plain default even when your brand sets one. null for the whole object means this template pins nothing.

Request

curl -X POST "https://api.rasket.com/templates" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "Welcome",
  "alias": "welcome",
  "subject": "Welcome, {{name}}",
  "html": "<p>Hello {{name}}, thanks for joining.</p>",
  "variables": [
    {
      "key": "name",
      "type": "string",
      "fallback_value": "there"
    }
  ]
}'

Response 201

{
  "id": "0199d2f1-4c4e-7a20-9c31-6f2b8a0e5d17",
  "object": "template"
}
  • A new template is a draft. Nothing can be sent from it until you publish it.
  • Send exactly one of html and starter. Neither is 422 missing_required_field; both is 422 invalid_parameter. The schema cannot express "exactly one", so a client will not catch it for you.
  • starter is one of the slugs GET /starters lists. Any other value is 400 validation_error.
  • With starter, anything else you send wins over the starter's: subject, text and variables override it, and alias, from and reply_to are yours to set. A text you send is kept as the plain text instead of the one generated from the layout. The copy keeps no link to the library, so improving a starter never changes it.
  • This route takes no Idempotency-Key. A retry with no alias creates a second template; set an alias, or check the list first.
  • A {{key}} the bodies use but variables does not declare — or a declared key the bodies never use — is 422 validation_error, with one entry in errors[] per key.
  • An alias another live template holds is 422 validation_error. Deleting a template frees its alias.
  • brand_overrides is not a send variable and declares nothing: a brand field is your project's and a {{key}} is the recipient's, so the declaration rule never sees one.
  • An unknown field name inside brand_overrides is 400 validation_error rather than ignored, and a font or button_style outside its list is too. Everything else it refuses is 422 invalid_parameter.

List templates

GET /templates

Every template, most recently changed first.

Query parameters

  • limitinteger

    How many items to return, 1–100. Defaults to 20.

  • afterstring

    Return the page that follows this item ID. Mutually exclusive with before.

  • beforestring

    Return the page that precedes this item ID. Mutually exclusive with after.

  • searchstring

    Only templates whose name or alias contains this, ignoring case. At most 200 characters.

  • statusstring

    Only templates in this status: draft or published.

Request

curl -X GET "https://api.rasket.com/templates?limit=20" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "id": "0199d2f1-4c4e-7a20-9c31-6f2b8a0e5d17",
      "name": "Welcome",
      "status": "published",
      "published_at": "2026-09-11T12:00:00.000Z",
      "created_at": "2026-09-11T12:00:00.000Z",
      "updated_at": "2026-09-11T12:00:00.000Z",
      "alias": "welcome"
    }
  ]
}
  • Items carry no content. Retrieve one template for its bodies and variables.
  • alias is present only on templates that have one.

Retrieve a template

GET /templates/{id}

By ID or by alias, with the current version's content.

Path parameters

  • idstringRequired

    The template's ID or its alias. An alias is matched case-insensitively.

Request

curl -X GET "https://api.rasket.com/templates/0199d2f1-4c4e-7a20-9c31-6f2b8a0e5d17" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "object": "template",
  "id": "0199d2f1-4c4e-7a20-9c31-6f2b8a0e5d17",
  "current_version_id": "0199d2f1-4c4e-7a20-9c31-6f2b8a0e5d18",
  "name": "Welcome",
  "alias": "welcome",
  "from": "Acme <hello@acme.com>",
  "subject": "Welcome, {{name}}",
  "reply_to": null,
  "html": "<p>Hello {{name}}, thanks for joining.</p>",
  "text": "Hello {{name}}, thanks for joining.",
  "variables": [
    {
      "id": "0199d2f1-4c4e-7a20-9c31-6f2b8a0e5d19",
      "key": "name",
      "type": "string",
      "fallback_value": "there",
      "created_at": "2026-09-11T12:00:00.000Z",
      "updated_at": "2026-09-11T12:00:00.000Z"
    }
  ],
  "brand_overrides": null,
  "created_at": "2026-09-11T12:00:00.000Z",
  "updated_at": "2026-09-11T12:00:00.000Z",
  "status": "published",
  "published_at": "2026-09-11T12:00:00.000Z",
  "has_unpublished_versions": false
}
  • The content shown is the **current** version — the open draft, if there is one. has_unpublished_versions: true means it differs from what sends use.
  • alias, from, subject and text are absent, not null, when unset.
  • brand_overrides is null when the template pins nothing, and carries only the fields it does pin — the rest come from your project brand.
  • A template that belongs to another team, or that has been deleted, is 404 not_found.

Update a template

PATCH /templates/{id}

Write the draft. Sends keep using the published version.

Path parameters

  • idstringRequired

    The template's ID or its alias. An alias is matched case-insensitively.

Body

  • namestring

    A new name.

  • aliasstring | null

    A new alias, or null to remove the current one.

  • fromstring | null

    The sender, as Name <address@domain> or a bare address. A send that names its own from overrides it. null clears it.

  • subjectstring | null

    The subject line. May carry {{key}} placeholders. null clears it.

  • htmlstring

    The HTML body. May carry {{key}} placeholders. On create, send this or starter — exactly one.

  • textstring

    The plain-text body. May carry {{key}} placeholders.

  • variablesobject[]

    The variables the bodies use, at most 200: { key, type, fallback_value }, with type one of string, number, boolean, object or list. Every {{key}} must be declared here, and every declared key must be used.

2 more fields (reply_to, brand_overrides)
  • reply_tostring[]

    Reply-to addresses.

  • brand_overridesobject | null

    This template's own values for your brand fields, for a one-off that should not wear the project brand: logo_url, primary_color, accent_color, font, button_style, company_name, address, footer_text. A field you leave out follows the project brand; a field set to null uses our plain default even when your brand sets one. null for the whole object means this template pins nothing.

Request

curl -X PATCH "https://api.rasket.com/templates/0199d2f1-4c4e-7a20-9c31-6f2b8a0e5d17" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0" \
  -H "Content-Type: application/json" \
  -d '{
  "subject": "Welcome aboard, {{name}}"
}'

Response 200

{
  "id": "0199d2f1-4c4e-7a20-9c31-6f2b8a0e5d17",
  "object": "template"
}
  • On a published template with no open draft, the first update starts a new version; later updates edit that draft in place. Publish it when it is ready.
  • Only the fields you send change. The declaration rule applies to the merged result: the bodies after your change must still declare and use exactly the same keys as variables.
  • An empty body is 422 invalid_parameter.
  • brand_overrides replaces what the template pinned rather than merging into it: send the whole set you want, or null to stop pinning anything. A per-field merge could not tell "stop pinning the accent colour" from "leave the accent colour alone".
  • On a template built in the dashboard's visual editor — which every copy of a starter is — an html that differs from the stored one switches it to code editing: the update starts a new version holding your HTML and no layout — even over an open draft — and keeps the plain text unless you send text too. Sending back the unchanged html switches nothing, and restoring an earlier version from the history brings the visual editor back.

Delete a template

DELETE /templates/{id}

Remove it and free its alias.

Path parameters

  • idstringRequired

    The template's ID or its alias. An alias is matched case-insensitively.

Request

curl -X DELETE "https://api.rasket.com/templates/0199d2f1-4c4e-7a20-9c31-6f2b8a0e5d17" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "object": "template",
  "id": "0199d2f1-4c4e-7a20-9c31-6f2b8a0e5d17",
  "deleted": true
}
  • The template answers 404 not_found from this moment, and a send that names it is 422 invalid_parameter.
  • Emails already sent from it keep their record of which version they used.

Publish a template

POST /templates/{id}/publish

Make the current version the one sends use.

Path parameters

  • idstringRequired

    The template's ID or its alias. An alias is matched case-insensitively.

Request

curl -X POST "https://api.rasket.com/templates/0199d2f1-4c4e-7a20-9c31-6f2b8a0e5d17/publish" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "id": "0199d2f1-4c4e-7a20-9c31-6f2b8a0e5d17",
  "object": "template"
}
  • Publishing takes effect for the next send. Emails already queued keep the version they resolved.
  • Publishing a template whose current version is already published changes nothing and still answers 200.

Duplicate a template

POST /templates/{id}/duplicate

A new draft holding a copy of the current version.

Path parameters

  • idstringRequired

    The template's ID or its alias. An alias is matched case-insensitively.

Request

curl -X POST "https://api.rasket.com/templates/0199d2f1-4c4e-7a20-9c31-6f2b8a0e5d17/duplicate" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "id": "0199d2f1-4c4e-7a20-9c31-6f2b8a0e5d2a",
  "object": "template"
}
  • The copy is named <name> (copy), has no alias, and is a draft. The source is untouched.
  • What is copied is the current version — the open draft, if there is one — not the published one.

Preview a template

POST /templates/{id}/preview

Render a version with values filled in, the way a send would. Nothing is stored.

Path parameters

  • idstringRequired

    The template's ID or its alias. An alias is matched case-insensitively.

Body

  • variablesobject

    Values by key, strings or numbers, as a send takes them. An undeclared key or a value of the wrong type is 422 invalid_parameter.

  • versioninteger

    The version number to render. The current version when absent.

Request

curl -X POST "https://api.rasket.com/templates/0199d2f1-4c4e-7a20-9c31-6f2b8a0e5d17/preview" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0" \
  -H "Content-Type: application/json" \
  -d '{
  "variables": {
    "name": "Ronald"
  }
}'

Response 200

{
  "object": "template_preview",
  "template_id": "0199d2f1-4c4e-7a20-9c31-6f2b8a0e5d17",
  "version_number": 1,
  "subject": "Welcome, Ronald",
  "html": "<p>Hello Ronald, thanks for joining.</p>",
  "text": "Hello Ronald, thanks for joining.",
  "missing_variables": []
}
  • A declared variable with neither a value nor a fallback is listed in missing_variables, and its placeholder is left in place.
  • html has been sanitised, with remote images withheld. Show it in a sandboxed iframe, never straight in a page.

Send a test of a template

POST /templates/{id}/test

Send the current version to up to five inboxes.

Path parameters

  • idstringRequired

    The template's ID or its alias. An alias is matched case-insensitively.

Headers

1 more field (Idempotency-Key)
  • Idempotency-Keystring

    Retries with the same key send the test once.

Body

  • tostring[]Required

    One to five addresses.

  • fromstring

    The sender for the test. Needed when the template has none.

  • variablesobject

    Values by key, strings or numbers. A variable you leave out uses its fallback; one without a fallback must be given.

Request

curl -X POST "https://api.rasket.com/templates/0199d2f1-4c4e-7a20-9c31-6f2b8a0e5d17/test" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0" \
  -H "Content-Type: application/json" \
  -d '{
  "to": ["you@acme.example"],
  "variables": {
    "name": "Ronald"
  }
}'

Response 202

{
  "object": "template_test",
  "template_id": "0199d2f1-4c4e-7a20-9c31-6f2b8a0e5d17",
  "version_number": 2,
  "data": [
    {
      "id": "4ef9a417-02e9-4d39-ad75-9611e0fcc33c",
      "to": "you@acme.example"
    }
  ]
}
  • The current version is sent: the open draft, if there is one. The subject starts with [Test] .
  • Each test counts as a send. The token also needs permission to send email.

List template versions

GET /templates/{id}/versions

Every version of a template, newest first.

Path parameters

  • idstringRequired

    The template's ID or its alias. An alias is matched case-insensitively.

Query parameters

  • limitinteger

    How many items to return, 1–100. Defaults to 20.

  • afterstring

    Return the page that follows this item ID. Mutually exclusive with before.

  • beforestring

    Return the page that precedes this item ID. Mutually exclusive with after.

Request

curl -X GET "https://api.rasket.com/templates/0199d2f1-4c4e-7a20-9c31-6f2b8a0e5d17/versions?limit=20" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "object": "template_version",
      "id": "0199d2f1-4c4e-7a20-9c31-6f2b8a0e5d18",
      "version_number": 1,
      "current": true,
      "published": true,
      "published_at": "2026-09-11T12:00:00.000Z",
      "created_at": "2026-09-11T12:00:00.000Z",
      "subject": "Welcome, {{name}}",
      "variables": []
    }
  ]
}
  • current is the version you read and edit; published is the one sends use.
  • Bodies are left out. Preview a template with version to see an older version's content.

Restore a template version

POST /templates/{id}/versions/{version_number}/restore

Make a new draft from an earlier version.

Path parameters

  • idstringRequired

    The template's ID or its alias. An alias is matched case-insensitively.

  • version_numberintegerRequired

    The version to copy.

Request

curl -X POST "https://api.rasket.com/templates/0199d2f1-4c4e-7a20-9c31-6f2b8a0e5d17/versions/{version_number}/restore" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "object": "template",
  "id": "0199d2f1-4c4e-7a20-9c31-6f2b8a0e5d17",
  "version_number": 3
}
  • Sends keep using the published version until you publish the new draft.
  • An unpublished draft you had open stays in the version list.

List the starter templates

GET /starters

The curated library a template can be started from.

Request

curl -X GET "https://api.rasket.com/starters" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "object": "starter",
      "slug": "receipt",
      "name": "Receipt",
      "summary": "What was paid, for what, and where to find the invoice.",
      "category": "Billing",
      "subject": "Your receipt from {{brand.company_name}}",
      "variables": [
        {
          "key": "amount",
          "type": "string"
        }
      ]
    }
  ]
}
  • The same catalogue for every project, in the order the dashboard shows it. It is not paginated.
  • Items carry no bodies. Retrieve one starter for its HTML and plain text.
  • Pass a slug to create a template as starter to copy it.

Retrieve a starter template

GET /starters/{slug}

One starter in full, with both bodies and sample values.

Path parameters

  • slugstringRequired

    The starter's slug.

Request

curl -X GET "https://api.rasket.com/starters/{slug}" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "object": "starter",
  "slug": "receipt",
  "name": "Receipt",
  "summary": "What was paid, for what, and where to find the invoice.",
  "category": "Billing",
  "subject": "Your receipt from {{brand.company_name}}",
  "variables": [
    {
      "key": "amount",
      "type": "string"
    }
  ],
  "html": "<p>You paid {{amount}}.</p>",
  "text": "You paid {{amount}}.",
  "sample_variables": {
    "amount": "$42.00"
  }
}
  • {{brand.*}} is your project's brand settings and resolves when the copy is rendered; {{key}} is a send variable and a copy declares every one of them.
  • sample_variables is for previewing only. It is never sent and never stored.
  • A slug that is not one of ours is 404 not_found.