List contacts

GET/api/v1/contacts

Contacts in the workspace, newest first, in pages of up to 100. Filter by exact email, free text, tag, company, owner or lifecycle stage. Pass nextCursor back as cursor for the next page; it is null on the last page.

Query parameters

ParameterTypeDescription
emailstringExact, case-insensitive email match. Returns at most one contact and ignores the other filters.
qstringMatches first name, last name, email or company name.
tagstringOnly contacts carrying this tag.
companyIdstringOnly contacts at this company.
ownerIdstringOnly contacts owned by this user.
lifecycleStagestringOnly contacts in this lifecycle stage, by value.
limitnumberPage size, 1 to 100. Default 50.
cursorstringThe nextCursor from the previous page.

Example request

curl "https://crowdstack.org/api/v1/contacts?tag=outbound-q4&limit=1" \
  -H "Authorization: Bearer $CROWDSTACK_API_KEY"

Example response

{
  "contacts": [
    {
      "id": "cmf3k2x9a0001abcd",
      "firstName": "Jane",
      "lastName": "Doe",
      "email": "jane@acme.com",
      "jobTitle": "Head of Growth",
      "phone": null,
      "website": "https://acme.com",
      "linkedinUrl": "https://linkedin.com/in/janedoe",
      "lifecycleStage": "lead",
      "tags": [
        "outbound-q4"
      ],
      "fields": {
        "demo_url": "https://example.com/demos/acme"
      },
      "owner": {
        "id": "cmf3k2x9a0003abcd",
        "name": "Jane Doe",
        "email": "jane@acme-mail.com"
      },
      "company": {
        "id": "cmf3k2x9a0002abcd",
        "name": "Acme",
        "domain": "acme.com"
      },
      "createdAt": "2026-09-15T10:12:00.000Z",
      "updatedAt": "2026-09-15T10:12:00.000Z"
    }
  ],
  "nextCursor": "cmf3k2x9a0001abcd"
}

Get a contact

GET/api/v1/contacts/:id

One contact by id, with counts of their tickets, deals, tasks and notes so you can decide what to fetch next.

Responses

StatusMeaning
404No contact with that id in this workspace.

Example request

curl "https://crowdstack.org/api/v1/contacts/cmf3k2x9a0001abcd" \
  -H "Authorization: Bearer $CROWDSTACK_API_KEY"

Example response

{
  "contact": {
    "id": "cmf3k2x9a0001abcd",
    "firstName": "Jane",
    "lastName": "Doe",
    "email": "jane@acme.com",
    "jobTitle": "Head of Growth",
    "phone": null,
    "website": "https://acme.com",
    "linkedinUrl": "https://linkedin.com/in/janedoe",
    "lifecycleStage": "lead",
    "tags": [
      "outbound-q4"
    ],
    "fields": {
      "demo_url": "https://example.com/demos/acme"
    },
    "owner": {
      "id": "cmf3k2x9a0003abcd",
      "name": "Jane Doe",
      "email": "jane@acme-mail.com"
    },
    "company": {
      "id": "cmf3k2x9a0002abcd",
      "name": "Acme",
      "domain": "acme.com"
    },
    "createdAt": "2026-09-15T10:12:00.000Z",
    "updatedAt": "2026-09-15T10:12:00.000Z",
    "counts": {
      "tickets": 2,
      "deals": 1,
      "tasks": 3,
      "notes": 4
    }
  }
}

Create or update a contact by email

POST/api/v1/contacts

Looks the contact up by email inside the workspace. Creates it if it does not exist, otherwise updates it. Keys you leave out are left alone, null clears a value, tags are added to the existing set, and custom fields merge key by key.

Custom field keys must exist under Settings, CRM, Fields. Unknown keys are rejected with 422 rather than dropped. Select fields accept either the option value or its label.

Request body

FieldTypeDescription
emailrequiredstringLower-cased and used as the match key.
firstNamestring | nullDefaults to the part of the email before @ for a new contact with no name.
lastNamestring | null—
jobTitlestring | null—
phonestring | nullStored as given; no formatting is applied.
websitestring | nullNormalised to a URL.
linkedinUrlstring | nullNormalised to a linkedin.com profile URL.
lifecycleStagestring | nullOne of the workspace's lifecycle stages, by value or label.
tagsstring[]Added to the contact's tags. Never removes existing tags.
fieldsobjectCustom field values keyed by field key. Unknown keys are rejected; get_workspace lists the defined fields.
companyobject | nullMatched by website domain, then case-insensitive name; created when neither matches. null unlinks the contact from its company.

Responses

StatusMeaning
201A new contact was created. Body: { contact, created: true }.
200An existing contact was updated. Body: { contact, created: false }.

Example request

curl -X POST https://crowdstack.org/api/v1/contacts \
  -H "Authorization: Bearer $CROWDSTACK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "jane@acme.com",
    "firstName": "Jane",
    "lastName": "Doe",
    "jobTitle": "Head of Growth",
    "lifecycleStage": "lead",
    "tags": [
      "outbound-q4"
    ],
    "company": {
      "name": "Acme",
      "website": "https://acme.com"
    }
  }'

Example response

{
  "created": true,
  "contact": {
    "id": "cmf3k2x9a0001abcd",
    "firstName": "Jane",
    "lastName": "Doe",
    "email": "jane@acme.com",
    "jobTitle": "Head of Growth",
    "phone": null,
    "website": "https://acme.com",
    "linkedinUrl": "https://linkedin.com/in/janedoe",
    "lifecycleStage": "lead",
    "tags": [
      "outbound-q4"
    ],
    "fields": {
      "demo_url": "https://example.com/demos/acme"
    },
    "owner": {
      "id": "cmf3k2x9a0003abcd",
      "name": "Jane Doe",
      "email": "jane@acme-mail.com"
    },
    "company": {
      "id": "cmf3k2x9a0002abcd",
      "name": "Acme",
      "domain": "acme.com"
    },
    "createdAt": "2026-09-15T10:12:00.000Z",
    "updatedAt": "2026-09-15T10:12:00.000Z"
  }
}

Update a contact

PATCH/api/v1/contacts/:id

Change any of a contact's fields by id. Only keys you send are changed; null clears a value. Use tags to replace the set, or addTags and removeTags to adjust it.

Request body

FieldTypeDescription
emailstring | nullChange the primary email. Must not belong to another contact. null removes it.
firstNamestring—
lastNamestring | null—
jobTitlestring | null—
phonestring | null—
websitestring | null—
linkedinUrlstring | null—
lifecycleStagestring | nullOne of the workspace's lifecycle stages, by value or label. null clears it.
tagsstring[]Replaces the whole tag set.
addTagsstring[]Tags to add to the existing set.
removeTagsstring[]Tags to remove from the existing set.
fieldsobjectCustom field values keyed by field key. Unknown keys are rejected; get_workspace lists the defined fields.
companyIdstring | nullLink the contact to this company. null unlinks.
ownerIdstring | nullThe member who owns this contact. null clears the owner.

Responses

StatusMeaning
200Body: { contact }.
404No contact with that id.
422A value did not validate, or the email belongs to another contact.

Example request

curl -X PATCH https://crowdstack.org/api/v1/contacts/cmf3k2x9a0001abcd \
  -H "Authorization: Bearer $CROWDSTACK_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "lifecycleStage": "customer",
    "addTags": [
      "won"
    ],
    "ownerId": "cmf3k2x9a0003abcd"
  }'

Example response

{
  "contact": {
    "id": "cmf3k2x9a0001abcd",
    "firstName": "Jane",
    "lastName": "Doe",
    "email": "jane@acme.com",
    "jobTitle": "Head of Growth",
    "phone": null,
    "website": "https://acme.com",
    "linkedinUrl": "https://linkedin.com/in/janedoe",
    "lifecycleStage": "customer",
    "tags": [
      "outbound-q4",
      "won"
    ],
    "fields": {
      "demo_url": "https://example.com/demos/acme"
    },
    "owner": {
      "id": "cmf3k2x9a0003abcd",
      "name": "Jane Doe",
      "email": "jane@acme-mail.com"
    },
    "company": {
      "id": "cmf3k2x9a0002abcd",
      "name": "Acme",
      "domain": "acme.com"
    },
    "createdAt": "2026-09-15T10:12:00.000Z",
    "updatedAt": "2026-09-15T10:12:00.000Z"
  }
}

Delete a contact

DELETE/api/v1/contacts/:id

Permanently deletes a contact along with their notes, activity and campaign memberships. Refused while the contact still has tickets; reassign or delete those first.

Responses

StatusMeaning
200Body: { deleted: true }.
404No contact with that id.
422The contact still has tickets.

Example request

curl -X DELETE https://crowdstack.org/api/v1/contacts/cmf3k2x9a0001abcd \
  -H "Authorization: Bearer $CROWDSTACK_API_KEY"

Example response

{
  "deleted": true
}

List a contact's activity

GET/api/v1/contacts/:id/activity

The contact's timeline, newest first: tickets opened, replies, campaign sends and replies, deal stage moves, tasks, subscription changes. Each entry names the product it came from and carries a short title, optional body and metadata such as the record id.

Query parameters

ParameterTypeDescription
productsupport | crm | tasks | revenue | campaignsOnly entries from this product.
limitnumberPage size, 1 to 100. Default 50.
cursorstringThe nextCursor from the previous page.

Example request

curl "https://crowdstack.org/api/v1/contacts/cmf3k2x9a0001abcd/activity" \
  -H "Authorization: Bearer $CROWDSTACK_API_KEY"

Example response

{
  "activity": [
    {
      "id": "cmf3k2x9a0070abcd",
      "product": "crm",
      "type": "deal.stage_changed",
      "title": "Deal moved to Proposal",
      "body": "Acme renewal: Qualified → Proposal",
      "meta": {
        "dealId": "cmf3k2x9a0080abcd"
      },
      "actor": {
        "id": "cmf3k2x9a0003abcd",
        "name": "Jane Doe",
        "email": "jane@acme-mail.com"
      },
      "occurredAt": "2026-09-15T10:12:00.000Z"
    }
  ],
  "nextCursor": null
}