Skip to content
On this site

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

POST /contacts

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

Body

Body
FieldTypeDescription
email*stringThe contact's address.
first_namestringUp to 200 characters.
last_namestringUp to 200 characters.
unsubscribedbooleanStart the contact opted out of every broadcast. Defaults to false. A repeat create never sets it back to false.
propertiesobjectValues for declared contact properties, keyed by property key. Each value must match its property's type.
2 more fields (segments, topics)
Body, less common
FieldTypeDescription
segmentsobject[]{ id } entries naming segments to add the contact to.
topicsobject[]{ id, subscription } entries, with subscription one of opt_in or opt_out.
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.

GET /contacts

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

Query parameters

Query parameters
FieldTypeDescription
segment_idstringOnly the contacts in this segment — the same membership a broadcast to it would resolve.
limitintegerHow many items to return, 1–100. Defaults to 20.
afterstringReturn the page that follows this item ID. Mutually exclusive with before.
beforestringReturn the page that precedes this item ID. Mutually exclusive with after.
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.

GET /contacts/{contact}

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

Path parameters

Path parameters
FieldTypeDescription
contact*stringThe contact's ID or its email address.
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.

PATCH /contacts/{contact}

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

Path parameters

Path parameters
FieldTypeDescription
contact*stringThe contact's ID or its email address.

Body

Body
FieldTypeDescription
emailstringA new address. It must not belong to another contact.
first_namestringUp to 200 characters.
last_namestringUp to 200 characters.
unsubscribedbooleanThe contact's global subscription state. true is a global unsubscribe and writes a consent record; false resubscribes.
propertiesobjectMerged key by key into the stored map. A null value removes the key.
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 /contacts/{contact}

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

Path parameters

Path parameters
FieldTypeDescription
contact*stringThe contact's ID or its email address.
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.

POST /contact-properties

Declare a typed key contacts can carry a value for.

Body

Body
FieldTypeDescription
key*stringUp to 50 characters, letters, digits and underscores, unique within the team. It is also the merge variable a broadcast can print.
type*stringstring or number.
fallback_valuestring | number | nullWhat a broadcast prints for a contact with no value. Must match type.
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.

GET /contact-properties

Every declared property, newest first.

Query parameters

Query parameters
FieldTypeDescription
limitintegerHow many items to return, 1–100. Defaults to 20.
afterstringReturn the page that follows this item ID. Mutually exclusive with before.
beforestringReturn the page that precedes this item ID. Mutually exclusive with after.
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": null,
      "created_at": "2026-09-08T22:22:17.595Z"
    }
  ]
}

GET /contact-properties/{contact_property_id}

One declaration by ID.

Path parameters

Path parameters
FieldTypeDescription
contact_property_id*stringThe contact property's ID.
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": null,
  "created_at": "2026-09-08T22:22:17.595Z"
}

PATCH /contact-properties/{contact_property_id}

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

Path parameters

Path parameters
FieldTypeDescription
contact_property_id*stringThe contact property's ID.

Body

Body
FieldTypeDescription
fallback_valuestring | number | nullThe new fallback, matching the property's type. null clears it.
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 /contact-properties/{contact_property_id}

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

Path parameters

Path parameters
FieldTypeDescription
contact_property_id*stringThe contact property's ID.
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.

GET /contacts/{contact}/segments

Every segment the contact is in right now.

Path parameters

Path parameters
FieldTypeDescription
contact*stringThe contact's ID or its email address.

Query parameters

Query parameters
FieldTypeDescription
limitintegerHow many items to return, 1–100. Defaults to 20.
afterstringReturn the page that follows this item ID. Mutually exclusive with before.
beforestringReturn the page that precedes this item ID. Mutually exclusive with after.
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.

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

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

Path parameters

Path parameters
FieldTypeDescription
contact*stringThe contact's ID or its email address.
segment*stringThe segment's ID.
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.

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

Remove an explicit membership.

Path parameters

Path parameters
FieldTypeDescription
contact*stringThe contact's ID or its email address.
segment*stringThe segment's ID.
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.

GET /contacts/{contact_id}/topics

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

Path parameters

Path parameters
FieldTypeDescription
contact_id*stringThe contact's ID or its email address.

Query parameters

Query parameters
FieldTypeDescription
limitintegerHow many items to return, 1–100. Defaults to 20.
afterstringReturn the page that follows this item ID. Mutually exclusive with before.
beforestringReturn the page that precedes this item ID. Mutually exclusive with after.
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.

PATCH /contacts/{contact_id}/topics

Record an explicit choice for each topic named.

Path parameters

Path parameters
FieldTypeDescription
contact_id*stringThe contact's ID or its email address.

Body

Body
FieldTypeDescription
topics*object[]{ id, subscription } entries, with subscription one of opt_in or opt_out.
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.