WorkloomDevelopers
v1Get an API key
Reference/Timelines, notes and tasks

Timelines, notes and tasks

What happened and what is next: timelines for contacts, companies and deals, notes on them, and tasks in your team’s queues.

Scopes: activity:read to read, activity:write to change.

The timeline entry object

attimestamp

When it happened.

kindstring

What it was: email, call, linkedin, meeting, stage, task, note, tell, visit, form, change…

channelstring, nullable

email, call, linkedin, sms, web or null.

directionstring, nullable

inbound or outbound, where it applies.

titlestring, nullable

A line saying what happened.

detailstring, nullable

More, when there is more.

contact_idstring, nullable

The contact it was with, on a company’s or deal’s timeline.

refobject, nullable

What it points at in Workloom: { type, id }, for example an email or a call.

The timeline entry object
{
  "at": "2026-10-06T14:02:11.000Z",
  "kind": "email",
  "channel": "email",
  "direction": "inbound",
  "title": "Re: pricing for 10 seats",
  "detail": "classified interested",
  "contact_id": "66f2a1c0e4b0a1b2c3d4e5f6",
  "ref": {
    "type": "email",
    "id": "66ff10c2e4b0a1b2c3d4ee01"
  }
}

The note object

idstring

The note’s id.

record_typestring

What it is on. One of contact, company, deal.

record_idstring

The record it is on.

bodystring

What it says. Markdown.

pinnedboolean

Pinned to the top of the record.

author_seat_idstring, nullable

Who wrote it. Null when an API key acting for the workspace did.

sourcestring, nullable

Where it was written, for example manual or api.

created_attimestamp

When it was written.

The note object
{
  "id": "66ff6e74e4b0a1b2c3d4bb01",
  "record_type": "contact",
  "record_id": "66f2a1c0e4b0a1b2c3d4e5f6",
  "body": "Asked for the security pack before the call.",
  "pinned": false,
  "author_seat_id": null,
  "source": "api",
  "created_at": "2026-10-07T09:20:00.000Z"
}

The task object

idstring

The task’s id.

typestring

callback, follow_up, custom, call, email_task, linkedin… Tasks made over the API are callback, follow_up or custom.

titlestring, nullable

What to do.

instructionsstring, nullable

Notes for whoever does it.

statusstring

Open until done or skipped. One of open, done, skipped.

due_attimestamp, nullable

When it is due.

contact_idstring, nullable

Who it is about.

owner_seat_idstring, nullable

Whose queue it is in.

sourcestring, nullable

What made it: manual, api, weave, campaign…

from_outreachboolean

True when a weave or a shuttle step made it. Those are finished in Workloom, where the step’s outcome is chosen.

completed_attimestamp, nullable

When it was finished.

created_attimestamp

When it was made.

The task object
{
  "id": "66ff7f85e4b0a1b2c3d4cc01",
  "type": "follow_up",
  "title": "Send the security pack",
  "instructions": null,
  "status": "open",
  "due_at": "2026-10-08T10:00:00.000Z",
  "contact_id": "66f2a1c0e4b0a1b2c3d4e5f6",
  "owner_seat_id": "66e0f1a2e4b0a1b2c3d40a0b",
  "source": "api",
  "from_outreach": false,
  "completed_at": null,
  "created_at": "2026-10-07T09:21:00.000Z"
}

Endpoints

GET/contacts/{id}/timeline

A contact’s timeline

What happened with a contact, newest first: emails, calls, LinkedIn, meetings, stage moves, finished tasks, website visits, Tells and more.

Scope: activity:read
Path parameters
idstringRequired

The contact’s id.

Query parameters
limitinteger

How many, 1 to 200. Default 50.

Errors
400

invalid_request, unknown_fields, reason_required or unsupported_version.

401

authentication_required or invalid_key.

403

missing_scope, forbidden, seat_inactive or organisation_inactive.

404

not_found.

429

rate_limited. Wait Retry-After seconds.

500

internal_error.

A contact’s timeline
curl "https://api.useworkloom.com/api/public/v1/contacts/66f2a1c0e4b0a1b2c3d4e5f6/timeline?limit=50" \
  -H "Authorization: Bearer $WORKLOOM_KEY"
const res = await fetch("https://api.useworkloom.com/api/public/v1/contacts/66f2a1c0e4b0a1b2c3d4e5f6/timeline?limit=50", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.WORKLOOM_KEY}`
  }
});
const data = await res.json();
import os, requests

res = requests.get(
    "https://api.useworkloom.com/api/public/v1/contacts/66f2a1c0e4b0a1b2c3d4e5f6/timeline?limit=50",
    headers={"Authorization": f"Bearer {os.environ['WORKLOOM_KEY']}"},
)
data = res.json()
Response
{
  "data": [
    {
      "at": "2026-10-06T14:02:11.000Z",
      "kind": "email",
      "channel": "email",
      "direction": "inbound",
      "title": "Re: pricing for 10 seats",
      "detail": "classified interested",
      "contact_id": "66f2a1c0e4b0a1b2c3d4e5f6",
      "ref": {
        "type": "email",
        "id": "66ff10c2e4b0a1b2c3d4ee01"
      }
    }
  ],
  "unavailable": []
}
200 OKsample
GET/companies/{id}/timeline

A company’s timeline

What happened across a company, newest first: its website visits and Tells, and the most active people’s timelines, each entry with its contact_id.

Scope: activity:read
Path parameters
idstringRequired

The company’s id.

Query parameters
limitinteger

How many, 1 to 200. Default 50.

Errors
400

invalid_request, unknown_fields, reason_required or unsupported_version.

401

authentication_required or invalid_key.

403

missing_scope, forbidden, seat_inactive or organisation_inactive.

404

not_found.

429

rate_limited. Wait Retry-After seconds.

500

internal_error.

A company’s timeline
curl "https://api.useworkloom.com/api/public/v1/companies/66f19b77e4b0a1b2c3d4a1b2/timeline?limit=50" \
  -H "Authorization: Bearer $WORKLOOM_KEY"
const res = await fetch("https://api.useworkloom.com/api/public/v1/companies/66f19b77e4b0a1b2c3d4a1b2/timeline?limit=50", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.WORKLOOM_KEY}`
  }
});
const data = await res.json();
import os, requests

res = requests.get(
    "https://api.useworkloom.com/api/public/v1/companies/66f19b77e4b0a1b2c3d4a1b2/timeline?limit=50",
    headers={"Authorization": f"Bearer {os.environ['WORKLOOM_KEY']}"},
)
data = res.json()
Response
{
  "data": [
    {
      "at": "2026-10-06T14:02:11.000Z",
      "kind": "email",
      "channel": "email",
      "direction": "inbound",
      "title": "Re: pricing for 10 seats",
      "detail": "classified interested",
      "contact_id": "66f2a1c0e4b0a1b2c3d4e5f6",
      "ref": {
        "type": "email",
        "id": "66ff10c2e4b0a1b2c3d4ee01"
      }
    }
  ],
  "unavailable": []
}
200 OKsample
GET/deals/{id}/timeline

A deal’s timeline

A deal’s stage moves, the changes made to it and its notes, newest first.

Scope: activity:read
Path parameters
idstringRequired

The deal’s id.

Query parameters
limitinteger

How many, 1 to 200. Default 50.

Errors
400

invalid_request, unknown_fields, reason_required or unsupported_version.

401

authentication_required or invalid_key.

403

missing_scope, forbidden, seat_inactive or organisation_inactive.

404

not_found.

429

rate_limited. Wait Retry-After seconds.

500

internal_error.

A deal’s timeline
curl "https://api.useworkloom.com/api/public/v1/deals/66f5c3d1e4b0a1b2c3d4c3d4/timeline?limit=50" \
  -H "Authorization: Bearer $WORKLOOM_KEY"
const res = await fetch("https://api.useworkloom.com/api/public/v1/deals/66f5c3d1e4b0a1b2c3d4c3d4/timeline?limit=50", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.WORKLOOM_KEY}`
  }
});
const data = await res.json();
import os, requests

res = requests.get(
    "https://api.useworkloom.com/api/public/v1/deals/66f5c3d1e4b0a1b2c3d4c3d4/timeline?limit=50",
    headers={"Authorization": f"Bearer {os.environ['WORKLOOM_KEY']}"},
)
data = res.json()
Response
{
  "data": [
    {
      "at": "2026-10-06T14:02:11.000Z",
      "kind": "stage",
      "channel": null,
      "direction": null,
      "title": "discovery → proposal",
      "detail": "By user",
      "contact_id": "66f2a1c0e4b0a1b2c3d4e5f6",
      "ref": null
    }
  ],
  "unavailable": []
}
200 OKsample
GET/notes

List notes on a record

Notes on one contact, company or deal, newest first.

Scope: activity:read
Query parameters
record_typestringRequired

contact, company or deal.

record_idstringRequired

The record.

limitinteger

How many to return, 1 to 100. Default 25.

cursorstring

The next_cursor from the previous page.

Errors
400

invalid_request, unknown_fields, reason_required or unsupported_version.

401

authentication_required or invalid_key.

403

missing_scope, forbidden, seat_inactive or organisation_inactive.

429

rate_limited. Wait Retry-After seconds.

500

internal_error.

List notes on a record
curl "https://api.useworkloom.com/api/public/v1/notes?record_type=contact&record_id=66f2a1c0e4b0a1b2c3d4e5f6&limit=25" \
  -H "Authorization: Bearer $WORKLOOM_KEY"
const res = await fetch("https://api.useworkloom.com/api/public/v1/notes?record_type=contact&record_id=66f2a1c0e4b0a1b2c3d4e5f6&limit=25", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.WORKLOOM_KEY}`
  }
});
const data = await res.json();
import os, requests

res = requests.get(
    "https://api.useworkloom.com/api/public/v1/notes?record_type=contact&record_id=66f2a1c0e4b0a1b2c3d4e5f6&limit=25",
    headers={"Authorization": f"Bearer {os.environ['WORKLOOM_KEY']}"},
)
data = res.json()
Response
{
  "data": [
    {
      "id": "66ff6e74e4b0a1b2c3d4bb01",
      "record_type": "contact",
      "record_id": "66f2a1c0e4b0a1b2c3d4e5f6",
      "body": "Asked for the security pack before the call.",
      "pinned": false,
      "author_seat_id": null,
      "source": "api",
      "created_at": "2026-10-07T09:20:00.000Z"
    }
  ],
  "has_more": true,
  "next_cursor": "66ff6e74e4b0a1b2c3d4bb01"
}
200 OKsample
POST/notes

Add a note

Adds a note to a contact, company or deal the key may edit. It shows on the record and its timeline, named as written by the API key.

Scope: activity:writeTakes Idempotency-Key
Header parameters
Idempotency-Keystring

Any unique string, up to 255 characters. Send the same key when you retry and the request runs once; the first answer comes back with Idempotent-Replayed: true. Kept 24 hours.

Body parameters
record_typestringRequired

What it goes on.

record_idstringRequired

The record.

bodystringRequired

What it says. Markdown, up to 20,000 characters.

pinnedboolean

Pin it to the top of the record.

Errors
400

invalid_request, unknown_fields, reason_required or unsupported_version.

401

authentication_required or invalid_key.

403

missing_scope, forbidden, seat_inactive or organisation_inactive.

409

duplicate, conflict or idempotency_key_in_use.

422

idempotency_key_reused.

429

rate_limited. Wait Retry-After seconds.

500

internal_error.

Add a note
curl -X POST "https://api.useworkloom.com/api/public/v1/notes" \
  -H "Authorization: Bearer $WORKLOOM_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "record_type": "contact",
  "record_id": "66f2a1c0e4b0a1b2c3d4e5f6",
  "body": "Asked for the security pack before the call."
}'
const res = await fetch("https://api.useworkloom.com/api/public/v1/notes", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.WORKLOOM_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID()
  },
  body: JSON.stringify({
    "record_type": "contact",
    "record_id": "66f2a1c0e4b0a1b2c3d4e5f6",
    "body": "Asked for the security pack before the call."
  })
});
const data = await res.json();
import os, uuid, requests

res = requests.post(
    "https://api.useworkloom.com/api/public/v1/notes",
    headers={"Authorization": f"Bearer {os.environ['WORKLOOM_KEY']}", "Idempotency-Key": str(uuid.uuid4())},
    json={
      "record_type": "contact",
      "record_id": "66f2a1c0e4b0a1b2c3d4e5f6",
      "body": "Asked for the security pack before the call."
    },
)
data = res.json()
Response
{
  "data": {
    "id": "66ff6e74e4b0a1b2c3d4bb01",
    "record_type": "contact",
    "record_id": "66f2a1c0e4b0a1b2c3d4e5f6",
    "body": "Asked for the security pack before the call.",
    "pinned": false,
    "author_seat_id": null,
    "source": "api",
    "created_at": "2026-10-07T09:20:00.000Z"
  },
  "livemode": true
}
201 Createdsample
GET/tasks

List tasks

Tasks in the queues the key reaches, oldest first.

Scope: activity:read
Query parameters
statusstring

open (default), done, skipped or all.

contact_idstring

Only tasks about this contact.

owner_seat_idstring

Only tasks in this teammate’s queue.

due_beforetimestamp

Only tasks due before this time (ISO 8601).

limitinteger

How many to return, 1 to 100. Default 25.

cursorstring

The next_cursor from the previous page.

Errors
400

invalid_request, unknown_fields, reason_required or unsupported_version.

401

authentication_required or invalid_key.

403

missing_scope, forbidden, seat_inactive or organisation_inactive.

429

rate_limited. Wait Retry-After seconds.

500

internal_error.

List tasks
curl "https://api.useworkloom.com/api/public/v1/tasks?status=open&owner_seat_id=66e0f1a2e4b0a1b2c3d40a0b&limit=25" \
  -H "Authorization: Bearer $WORKLOOM_KEY"
const res = await fetch("https://api.useworkloom.com/api/public/v1/tasks?status=open&owner_seat_id=66e0f1a2e4b0a1b2c3d40a0b&limit=25", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.WORKLOOM_KEY}`
  }
});
const data = await res.json();
import os, requests

res = requests.get(
    "https://api.useworkloom.com/api/public/v1/tasks?status=open&owner_seat_id=66e0f1a2e4b0a1b2c3d40a0b&limit=25",
    headers={"Authorization": f"Bearer {os.environ['WORKLOOM_KEY']}"},
)
data = res.json()
Response
{
  "data": [
    {
      "id": "66ff7f85e4b0a1b2c3d4cc01",
      "type": "follow_up",
      "title": "Send the security pack",
      "instructions": null,
      "status": "open",
      "due_at": "2026-10-08T10:00:00.000Z",
      "contact_id": "66f2a1c0e4b0a1b2c3d4e5f6",
      "owner_seat_id": "66e0f1a2e4b0a1b2c3d40a0b",
      "source": "api",
      "from_outreach": false,
      "completed_at": null,
      "created_at": "2026-10-07T09:21:00.000Z"
    }
  ],
  "has_more": true,
  "next_cursor": "66ff7f85e4b0a1b2c3d4cc01"
}
200 OKsample
POST/tasks

Add a task

Puts a callback, follow-up or task of your own into someone’s queue: the owner you name, else the contact’s owner, else the person the key acts for.

Scope: activity:writeTakes Idempotency-Key
Header parameters
Idempotency-Keystring

Any unique string, up to 255 characters. Send the same key when you retry and the request runs once; the first answer comes back with Idempotent-Replayed: true. Kept 24 hours.

Body parameters
typestringRequired

callback: call them back. follow_up: get back to them. custom: anything else.

titlestringRequired

What to do.

instructionsstring, nullable

Notes for whoever does it.

contact_idstring, nullable

Who it is about.

due_attimestamp, nullable

When it is due. Defaults to now.

owner_seat_idstring, nullable

Whose queue it goes in. Defaults to the contact’s owner, then the person the key acts for.

Errors
400

invalid_request, unknown_fields, reason_required or unsupported_version.

401

authentication_required or invalid_key.

403

missing_scope, forbidden, seat_inactive or organisation_inactive.

409

duplicate, conflict or idempotency_key_in_use.

422

idempotency_key_reused.

429

rate_limited. Wait Retry-After seconds.

500

internal_error.

Add a task
curl -X POST "https://api.useworkloom.com/api/public/v1/tasks" \
  -H "Authorization: Bearer $WORKLOOM_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{
  "type": "follow_up",
  "title": "Send the security pack",
  "contact_id": "66f2a1c0e4b0a1b2c3d4e5f6",
  "due_at": "2026-10-08T10:00:00Z"
}'
const res = await fetch("https://api.useworkloom.com/api/public/v1/tasks", {
  method: "POST",
  headers: {
    Authorization: `Bearer ${process.env.WORKLOOM_KEY}`,
    "Content-Type": "application/json",
    "Idempotency-Key": crypto.randomUUID()
  },
  body: JSON.stringify({
    "type": "follow_up",
    "title": "Send the security pack",
    "contact_id": "66f2a1c0e4b0a1b2c3d4e5f6",
    "due_at": "2026-10-08T10:00:00Z"
  })
});
const data = await res.json();
import os, uuid, requests

res = requests.post(
    "https://api.useworkloom.com/api/public/v1/tasks",
    headers={"Authorization": f"Bearer {os.environ['WORKLOOM_KEY']}", "Idempotency-Key": str(uuid.uuid4())},
    json={
      "type": "follow_up",
      "title": "Send the security pack",
      "contact_id": "66f2a1c0e4b0a1b2c3d4e5f6",
      "due_at": "2026-10-08T10:00:00Z"
    },
)
data = res.json()
Response
{
  "data": {
    "id": "66ff7f85e4b0a1b2c3d4cc01",
    "type": "follow_up",
    "title": "Send the security pack",
    "instructions": null,
    "status": "open",
    "due_at": "2026-10-08T10:00:00.000Z",
    "contact_id": "66f2a1c0e4b0a1b2c3d4e5f6",
    "owner_seat_id": "66e0f1a2e4b0a1b2c3d40a0b",
    "source": "api",
    "from_outreach": false,
    "completed_at": null,
    "created_at": "2026-10-07T09:21:00.000Z"
  },
  "livemode": true
}
201 Createdsample
GET/tasks/{id}

Get a task

Scope: activity:read
Path parameters
idstringRequired

The task’s id.

Errors
400

invalid_request, unknown_fields, reason_required or unsupported_version.

401

authentication_required or invalid_key.

403

missing_scope, forbidden, seat_inactive or organisation_inactive.

404

not_found.

429

rate_limited. Wait Retry-After seconds.

500

internal_error.

Get a task
curl "https://api.useworkloom.com/api/public/v1/tasks/66ff7f85e4b0a1b2c3d4cc01" \
  -H "Authorization: Bearer $WORKLOOM_KEY"
const res = await fetch("https://api.useworkloom.com/api/public/v1/tasks/66ff7f85e4b0a1b2c3d4cc01", {
  method: "GET",
  headers: {
    Authorization: `Bearer ${process.env.WORKLOOM_KEY}`
  }
});
const data = await res.json();
import os, requests

res = requests.get(
    "https://api.useworkloom.com/api/public/v1/tasks/66ff7f85e4b0a1b2c3d4cc01",
    headers={"Authorization": f"Bearer {os.environ['WORKLOOM_KEY']}"},
)
data = res.json()
Response
{
  "data": {
    "id": "66ff7f85e4b0a1b2c3d4cc01",
    "type": "follow_up",
    "title": "Send the security pack",
    "instructions": null,
    "status": "open",
    "due_at": "2026-10-08T10:00:00.000Z",
    "contact_id": "66f2a1c0e4b0a1b2c3d4e5f6",
    "owner_seat_id": "66e0f1a2e4b0a1b2c3d40a0b",
    "source": "api",
    "from_outreach": false,
    "completed_at": null,
    "created_at": "2026-10-07T09:21:00.000Z"
  }
}
200 OKsample
PATCH/tasks/{id}

Finish or reschedule a task

Marks a task done or skipped, reopens it, or moves its due time. A step of a weave or a shuttle (from_outreach: true) is finished in Workloom, where its outcome is chosen, and answers conflict here.

Scope: activity:write
Path parameters
idstringRequired

The task’s id.

Body parameters
statusstring

done or skipped finishes it; open reopens it.

due_attimestamp, nullable

A new due time.

Errors
400

invalid_request, unknown_fields, reason_required or unsupported_version.

401

authentication_required or invalid_key.

403

missing_scope, forbidden, seat_inactive or organisation_inactive.

404

not_found.

409

duplicate, conflict or idempotency_key_in_use.

429

rate_limited. Wait Retry-After seconds.

500

internal_error.

Finish or reschedule a task
curl -X PATCH "https://api.useworkloom.com/api/public/v1/tasks/66ff7f85e4b0a1b2c3d4cc01" \
  -H "Authorization: Bearer $WORKLOOM_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "status": "done"
}'
const res = await fetch("https://api.useworkloom.com/api/public/v1/tasks/66ff7f85e4b0a1b2c3d4cc01", {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${process.env.WORKLOOM_KEY}`,
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    "status": "done"
  })
});
const data = await res.json();
import os, requests

res = requests.patch(
    "https://api.useworkloom.com/api/public/v1/tasks/66ff7f85e4b0a1b2c3d4cc01",
    headers={"Authorization": f"Bearer {os.environ['WORKLOOM_KEY']}"},
    json={
      "status": "done"
    },
)
data = res.json()
Response
{
  "data": {
    "id": "66ff7f85e4b0a1b2c3d4cc01",
    "type": "follow_up",
    "title": "Send the security pack",
    "instructions": null,
    "status": "open",
    "due_at": "2026-10-08T10:00:00.000Z",
    "contact_id": "66f2a1c0e4b0a1b2c3d4e5f6",
    "owner_seat_id": "66e0f1a2e4b0a1b2c3d40a0b",
    "source": "api",
    "from_outreach": false,
    "completed_at": null,
    "created_at": "2026-10-07T09:21:00.000Z"
  },
  "livemode": true
}
200 OKsample
Was this page useful?