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.
  • VercelGuidesAdd Rasket on Vercel: a Sending key in each project as RASKET_API_KEY, no copying.
  • 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 referenceContacts

Contacts

The people a broadcast can go to: an address, optional names, typed properties, the segments they are in and the topics they have chosen.

Addressing a contact

  • Every {contact} below accepts the contact's ID or its email address. Matching on the address is case-insensitive.
  • Creating a contact whose address already exists updates that contact and answers with its existing id. There is no duplicate error, and a repeat create never resubscribes someone who opted out.
  • Reads work with either key permission. Creating, updating and deleting need a full_access key.

Properties

A property is declared once — a key and a type — and then any contact can carry a value for it. Values are checked against the type on every write, and the key is also the merge variable a broadcast prints: {{{PLAN}}} for a property called plan, falling back to the property's fallback_value for a contact without one.

Unsubscribing and erasure

  • unsubscribed: true is a global opt-out: the contact receives no broadcast at all, whatever their topics say. Every change to it, and every topic choice, writes a consent record with its source and time.
  • DELETE is an erasure, not a soft delete. The personal data goes; the consent records stay with a hash of the address, so an opt-out remains provable after the person is gone.

Events

Three webhook types are sent for contacts. A CSV import sends none of them per row.

Contact webhook events
TypeSent when
contact.createdA contact was created through the API.
contact.updatedA contact's fields or properties changed through the API. Not sent for a topic or segment change, or for an unsubscribe made on the preference page.
contact.deletedA contact was erased. The payload carries the contact as it was.

Endpoints

Create a contact

POST /contacts

Add an address to your audience, with optional names, properties, segments and topics.

Body

  • emailstringRequired

    The contact's address.

  • first_namestring

    Up to 200 characters.

  • last_namestring

    Up to 200 characters.

  • unsubscribedboolean

    Start the contact opted out of every broadcast. Defaults to false. A repeat create never sets it back to false.

  • propertiesobject

    Values for declared contact properties, keyed by property key. Each value must match its property's type.

2 more fields (segments, topics)
  • segmentsobject[]

    { id } entries naming segments to add the contact to.

  • topicsobject[]

    { id, subscription } entries, with subscription one of opt_in or opt_out.

Request

curl -X POST "https://api.rasket.com/contacts" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0" \
  -H "Content-Type: application/json" \
  -d '{
  "email": "ronald.williams@example.com",
  "first_name": "Ronald",
  "last_name": "Williams",
  "properties": {
    "company": "Acme",
    "seats": 12
  }
}'

Response 201

{
  "object": "contact",
  "id": "e169aa45-1ecf-4183-9955-b1499d5701d3"
}
  • An address the team already holds a contact for is updated with the fields the body carries, and the existing id is returned. There is no 409.
  • Every key in properties must name a declared contact property of a matching type; an unknown key or a wrong type is 422 invalid_parameter naming the key.
  • Your plan's contact limit is enforced here and on imports: past it, the call is 422 validation_error with Contact limit reached.
  • Emits contact.created — or contact.updated when the address already existed.

List contacts

GET /contacts

Every contact, newest first; optionally only the members of one segment.

Query parameters

  • segment_idstring

    Only the contacts in this segment — the same membership a broadcast to it would resolve.

  • 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/contacts?limit=20" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "object": "list",
  "data": [
    {
      "id": "e169aa45-1ecf-4183-9955-b1499d5701d3",
      "email": "ronald.williams@example.com",
      "first_name": "Ronald",
      "last_name": "Williams",
      "created_at": "2026-09-08T22:22:17.595Z",
      "unsubscribed": false
    }
  ]
}
  • List items carry no properties; retrieve a contact for them.
  • The envelope carries no has_more. Walk pages with after, using the last item's id; an empty page is the end.

Retrieve a contact

GET /contacts/{contact}

One contact, by ID or by address, with its properties.

Path parameters

  • contactstringRequired

    The contact's ID or its email address.

Request

curl -X GET "https://api.rasket.com/contacts/e169aa45-1ecf-4183-9955-b1499d5701d3" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "object": "contact",
  "id": "e169aa45-1ecf-4183-9955-b1499d5701d3",
  "email": "ronald.williams@example.com",
  "first_name": "Ronald",
  "last_name": "Williams",
  "created_at": "2026-09-08T22:22:17.595Z",
  "unsubscribed": false,
  "properties": {
    "company": "Acme",
    "seats": 12
  }
}
  • A contact in another team is 404 not_found, indistinguishable from one that does not exist.

Update a contact

PATCH /contacts/{contact}

Change any of the fields; only what the body carries is touched.

Path parameters

  • contactstringRequired

    The contact's ID or its email address.

Body

  • emailstring

    A new address. It must not belong to another contact.

  • first_namestring

    Up to 200 characters.

  • last_namestring

    Up to 200 characters.

  • unsubscribedboolean

    The contact's global subscription state. true is a global unsubscribe and writes a consent record; false resubscribes.

  • propertiesobject

    Merged key by key into the stored map. A null value removes the key.

Request

curl -X PATCH "https://api.rasket.com/contacts/e169aa45-1ecf-4183-9955-b1499d5701d3" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0" \
  -H "Content-Type: application/json" \
  -d '{
  "last_name": "Williams-Smith",
  "properties": {
    "seats": 15
  }
}'

Response 200

{
  "object": "contact",
  "id": "e169aa45-1ecf-4183-9955-b1499d5701d3"
}
  • Emits contact.updated. Changing the contact's topics or segments through their own routes does not.

Delete a contact

DELETE /contacts/{contact}

Erase the contact's personal data. Not a soft delete.

Path parameters

  • contactstringRequired

    The contact's ID or its email address.

Request

curl -X DELETE "https://api.rasket.com/contacts/e169aa45-1ecf-4183-9955-b1499d5701d3" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "object": "contact",
  "id": "e169aa45-1ecf-4183-9955-b1499d5701d3",
  "deleted": true
}
  • The address and names are removed and the contact is 404 from that moment. Consent records are kept with a hash of the address only, so a later opt-out can still be proven.
  • Emits contact.deleted, carrying the contact as it was.

Create a contact property

POST /contact-properties

Declare a typed key contacts can carry a value for.

Body

  • keystringRequired

    Up to 50 characters, letters, digits and underscores, unique within the team. It is also the merge variable a broadcast can print.

  • typestringRequired

    string or number.

  • fallback_valuestring | number | null

    What a broadcast prints for a contact with no value. Must match type.

Request

curl -X POST "https://api.rasket.com/contact-properties" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0" \
  -H "Content-Type: application/json" \
  -d '{
  "key": "company",
  "type": "string"
}'

Response 201

{
  "object": "contact_property",
  "id": "b6d24b8e-af0b-4c3c-be0c-359bbd97381e"
}
  • A key that already exists is 422 invalid_parameter.
  • boolean properties exist too, but arrive only through a CSV import's column map.

List contact properties

GET /contact-properties

Every declared property, newest 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.

Request

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

Response 200

{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "id": "b6d24b8e-af0b-4c3c-be0c-359bbd97381e",
      "key": "company",
      "type": "string",
      "fallback_value": "your company",
      "created_at": "2026-09-08T22:22:17.595Z"
    }
  ]
}

Retrieve a contact property

GET /contact-properties/{contact_property_id}

One declaration by ID.

Path parameters

  • contact_property_idstringRequired

    The contact property's ID.

Request

curl -X GET "https://api.rasket.com/contact-properties/b6d24b8e-af0b-4c3c-be0c-359bbd97381e" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "object": "contact_property",
  "id": "b6d24b8e-af0b-4c3c-be0c-359bbd97381e",
  "key": "company",
  "type": "string",
  "fallback_value": "your company",
  "created_at": "2026-09-08T22:22:17.595Z"
}
  • fallback_value is absent, not null, on a property that has none.

Update a contact property

PATCH /contact-properties/{contact_property_id}

Change the fallback value. The key and the type are fixed.

Path parameters

  • contact_property_idstringRequired

    The contact property's ID.

Body

  • fallback_valuestring | number | null

    The new fallback, matching the property's type. null clears it.

Request

curl -X PATCH "https://api.rasket.com/contact-properties/b6d24b8e-af0b-4c3c-be0c-359bbd97381e" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0" \
  -H "Content-Type: application/json" \
  -d '{
  "fallback_value": "your company"
}'

Response 200

{
  "object": "contact_property",
  "id": "b6d24b8e-af0b-4c3c-be0c-359bbd97381e"
}

Delete a contact property

DELETE /contact-properties/{contact_property_id}

Remove the declaration, and the key from every contact that had a value.

Path parameters

  • contact_property_idstringRequired

    The contact property's ID.

Request

curl -X DELETE "https://api.rasket.com/contact-properties/b6d24b8e-af0b-4c3c-be0c-359bbd97381e" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "object": "contact_property",
  "id": "b6d24b8e-af0b-4c3c-be0c-359bbd97381e",
  "deleted": true
}
  • A segment whose filter names the property stops matching on it.

List a contact's segments

GET /contacts/{contact}/segments

Every segment the contact is in right now.

Path parameters

  • contactstringRequired

    The contact's ID or its email address.

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/contacts/e169aa45-1ecf-4183-9955-b1499d5701d3/segments" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "id": "78261eea-8f8b-4381-83c6-79fa7120f1cf",
      "name": "Trial accounts",
      "created_at": "2026-09-08T22:22:17.595Z"
    }
  ]
}
  • Both kinds of membership are listed: segments the contact was added to explicitly and segments whose filter it matches. created_at is when the contact was added for the first, and the later of the two creations for the second.

Add a contact to a segment

POST /contacts/{contact}/segments/{segment}

An explicit membership, alongside whatever the segment's filter matches.

Path parameters

  • contactstringRequired

    The contact's ID or its email address.

  • segmentstringRequired

    The segment's ID.

Request

curl -X POST "https://api.rasket.com/contacts/e169aa45-1ecf-4183-9955-b1499d5701d3/segments/78261eea-8f8b-4381-83c6-79fa7120f1cf" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "object": "contact_segment",
  "contact_id": "e169aa45-1ecf-4183-9955-b1499d5701d3",
  "segment_id": "78261eea-8f8b-4381-83c6-79fa7120f1cf"
}
  • Adding a contact that is already a member changes nothing and is not an error.

Remove a contact from a segment

DELETE /contacts/{contact}/segments/{segment}

Remove an explicit membership.

Path parameters

  • contactstringRequired

    The contact's ID or its email address.

  • segmentstringRequired

    The segment's ID.

Request

curl -X DELETE "https://api.rasket.com/contacts/e169aa45-1ecf-4183-9955-b1499d5701d3/segments/78261eea-8f8b-4381-83c6-79fa7120f1cf" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "object": "contact_segment",
  "contact_id": "e169aa45-1ecf-4183-9955-b1499d5701d3",
  "segment_id": "78261eea-8f8b-4381-83c6-79fa7120f1cf",
  "deleted": true
}
  • A contact the segment's filter matches stays a member: a filter is a rule, not a list. To exclude someone, change the filter or unsubscribe them.

List a contact's topics

GET /contacts/{contact_id}/topics

Every live topic, with the contact's effective subscription to each.

Path parameters

  • contact_idstringRequired

    The contact's ID or its email address.

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/contacts/e169aa45-1ecf-4183-9955-b1499d5701d3/topics" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0"

Response 200

{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "id": "b6d24b8e-af0b-4c3c-be0c-359bbd97381e",
      "name": "Product updates",
      "description": "What shipped this month.",
      "subscription": "opt_in"
    }
  ]
}
  • subscription is the contact's own choice where one was recorded, and the topic's default_subscription otherwise.

Update a contact's topics

PATCH /contacts/{contact_id}/topics

Record an explicit choice for each topic named.

Path parameters

  • contact_idstringRequired

    The contact's ID or its email address.

Body

  • topicsobject[]Required

    { id, subscription } entries, with subscription one of opt_in or opt_out.

Request

curl -X PATCH "https://api.rasket.com/contacts/e169aa45-1ecf-4183-9955-b1499d5701d3/topics" \
  -H "Authorization: Bearer $RASKET_API_KEY" \
  -H "User-Agent: acme-billing/1.0" \
  -H "Content-Type: application/json" \
  -d '{
  "topics": [
    {
      "id": "b6d24b8e-af0b-4c3c-be0c-359bbd97381e",
      "subscription": "opt_out"
    }
  ]
}'

Response 200

{
  "object": "contact_topics",
  "contact_id": "e169aa45-1ecf-4183-9955-b1499d5701d3",
  "topics": [
    {
      "id": "b6d24b8e-af0b-4c3c-be0c-359bbd97381e",
      "subscription": "opt_out"
    }
  ]
}
  • Every change writes a consent record with its source and time, which is what makes an opt-out provable later.
  • A topic id that is not a live topic of the team is 422 invalid_parameter.