Read

Every read endpoint: one company, events, people, content, tables.

One company

Does this id resolve? Always 200: matched, seen-but-unresolved, or never seen.

Parameters
externalIdTypedomainAny registered link type: slack_channel_id, stripe_customer_id, anything.
externalIdValuerequiredpiedpiper.comThe id itself.
curl -s "$ACKDB_URL/entities/lookup?externalIdType=domain&externalIdValue=piedpiper.com" \
  -H "Authorization: Bearer $ACKDB_API_KEY"
Response · 200
{
  "data": {
    "match": {
      "entityId": "01HXYZ123456789ABCDEFGH",
      "entityName": "acme.com",
      "domain": "acme.com",
      "linkedAt": "2026-08-01T00:00:00Z"
    },
    "unresolvedMatches": [
      {
        "unresolvedContentId": "…",
        "source": "…",
        "timestamp": "2026-08-01T00:00:00Z"
      }
    ]
  }
}

Get aggregated entity profile

Parameters
entityIdrequiredEntity ID (ULID)
curl -s "$ACKDB_URL/entities/{entityId}/profile" \
  -H "Authorization: Bearer $ACKDB_API_KEY"
Response · 200
{
  "data": {
    "entity": {
      "entityId": "01HXYZ123456789ABCDEFGH",
      "name": "Acme Corp",
      "createdAt": "2026-08-01T00:00:00Z",
      "updatedAt": "2026-08-01T00:00:00Z"
    },
    "identityLinks": [
      {
        "entityId": "01HXYZ123456789ABCDEFGH",
        "source": "slack",
        "externalIdType": "slack_channel_id",
        "externalIdValue": "C04ABC",
        "confidence": 1,
        "linkedAt": "2026-08-01T00:00:00Z"
      }
    ],
    "traits": [
      {
        "entityId": "01HXYZ123456789ABCDEFGH",
        "traitKey": "slack_message_count",
        "valueString": null,
        "valueNumber": 42,
        "valueBool": null,
        "valueDate": null,
        "source": "slack",
        "workflow": "slack-ingestion",
        "confidence": 1,
        "updatedAt": "2026-08-01T00:00:00Z"
      }
    ],
    "recentEvents": [
      {
        "id": "01HXYZ123456789ABCDEFGH",
        "entityId": "01HXYZ123456789ABCDEFGI",
        "source": "slack",
        "eventType": "message",
        "summary": "Customer asked about pricing in #acme-corp",
        "metadata": {},
        "timestamp": "2026-08-01T00:00:00Z",
        "createdAt": "2026-08-01T00:00:00Z"
      }
    ]
  }
}

Get the full entity object by identity link

Parameters
externalIdValuerequiredThe identifier to resolve - a domain, slack_channel_id, stripe_customer_id, email, etc.
externalIdTypedomainOPTIONAL.
sourceOPTIONAL.
eventsLimit50Max events to return (0–500, default 50; 0 omits the collection).
eventsOffset0Event pagination offset (default 0).
eventsSourceFilter events by source (e.g.
eventsTypeFilter events by event type (e.g.
eventsSinceOnly events at/after this ISO timestamp.
eventsUntilOnly events at/before this ISO timestamp.
rawLimit20Max raw payloads (0–500, default 20; 0 omits).
rawOffset0Raw payload pagination offset (default 0).
conversationsLimit20Max conversations (0–200, default 20; 0 omits).
conversationsOffset0Conversation pagination offset (default 0).
conversationsSourceFilter conversations by source.
conversationsTopicFilter conversations by topic.
conversationsSentimentFilter conversations by sentiment.
conversationsSinceOnly conversations at/after this ISO timestamp.
conversationsUntilOnly conversations at/before this ISO timestamp.
filesLimit50Max files (0–500, default 50; 0 omits).
filesOffset0File pagination offset (default 0).
filesSourceFilter files by source.
filesTypeFilter files by file type.
filesProcessedFilter by processed state (true/false).
contactstrueSet false to omit the contacts list.
conversationSummarytrueSet false to omit the conversation rollup.
curl -s "$ACKDB_URL/entities/full?externalIdType=domain&eventsLimit=50&eventsOffset=0&rawLimit=20&rawOffset=0&conversationsLimit=20&conversationsOffset=0&filesLimit=50&filesOffset=0&contacts=true&conversationSummary=true" \
  -H "Authorization: Bearer $ACKDB_API_KEY"
Response · 200
{
  "data": {
    "entity": {
      "entityId": "01HXYZ123456789ABCDEFGH",
      "name": "Acme Corp",
      "createdAt": "2026-08-01T00:00:00Z",
      "updatedAt": "2026-08-01T00:00:00Z"
    },
    "identityLinks": [
      {
        "entityId": "01HXYZ123456789ABCDEFGH",
        "source": "slack",
        "externalIdType": "slack_channel_id",
        "externalIdValue": "C04ABC",
        "confidence": 1,
        "linkedAt": "2026-08-01T00:00:00Z"
      }
    ],
    "traits": [
      {
        "entityId": "01HXYZ123456789ABCDEFGH",
        "traitKey": "slack_message_count",
        "valueString": null,
        "valueNumber": 42,
        "valueBool": null,
        "valueDate": null,
        "source": "slack",
        "workflow": "slack-ingestion"
      }
    ],
    "contacts": [
      {}
    ],
    "events": {
      "data": [
        {}
      ],
      "total": 0,
      "limit": 0,
      "offset": 0
    },
    "rawPayloads": {
      "data": [
        {}
      ],
      "total": 0,
      "limit": 0,
      "offset": 0
    },
    "conversations": {
      "data": [
        {}
      ],
      "total": 0,
      "limit": 0,
      "offset": 0
    },
    "files": {
      "data": [
        {}
      ],
      "total": 0,
      "limit": 0,
      "offset": 0
    }
  },
  "resolvedBy": {
    "externalIdValue": "…",
    "externalIdType": "…",
    "source": "…"
  }
}

Fuzzy search → the single closest company (hydrated like /full)

Parameters
qFree text to match against the entity name + all identity-link values (domain, slack_channel_id, stripe_customer_id, …).
nameMatch against the company name only.
domainMatch against the company's domain identity links only.
identityMatch against any identity-link value (Stripe customer ID, Slack channel ID, CRM account ID, …).
identityTypeRestrict identity to one identity-link type (e.g.
contactLimitCap on nested contacts (0–1000).
curl -s "$ACKDB_URL/entities/search" \
  -H "Authorization: Bearer $ACKDB_API_KEY"
Response · 200
{
  "data": {},
  "matchedOn": {
    "field": "name",
    "value": "…",
    "tier": "exact"
  }
}

List and filter entities

Parameters
searchSearch filter applied to entity name (case-insensitive partial match).
traitFilterTrait filter in traitKey:operator:value format.
limit50Maximum number of entities to return.
offset0Number of entities to skip for pagination.
includeReferencefalseInclude reference companies — those whose only ingested evidence comes from a contextOnly source (job history, education).
curl -s "$ACKDB_URL/entities?limit=50&offset=0&includeReference=false" \
  -H "Authorization: Bearer $ACKDB_API_KEY"
Response · 200
{
  "data": [
    {
      "entityId": "01HXYZ123456789ABCDEFGH",
      "name": "Acme Corp",
      "createdAt": "2026-08-01T00:00:00Z",
      "updatedAt": "2026-08-01T00:00:00Z"
    }
  ],
  "pagination": {
    "total": 150,
    "limit": 50,
    "offset": 0,
    "totalIsLowerBound": true
  }
}

/full needs no externalIdType — a value maps to one company. /search ranks substrings (exact beats prefix beats contains), no typo tolerance. GET /entities with traitFilter= returns bare rows — hydrate via /profile.

Events

Get activity timeline for an entity

Parameters
entityIdrequiredEntity ID (ULID)
limit20Maximum number of events to return.
offset0Number of events to skip for pagination.
sourceFilter by source type (e.g.
sourcesCSV include list — only events from these sources (e.g.
excludeSourcesCSV exclude list — drop events from these sources (e.g.
eventTypeFilter by event type (e.g.
sinceFilter events after this ISO date.
untilFilter events before this ISO date.
curl -s "$ACKDB_URL/entities/{entityId}/events?limit=20&offset=0" \
  -H "Authorization: Bearer $ACKDB_API_KEY"
Response · 200
{
  "data": [
    {
      "id": "01HXYZ123456789ABCDEFGH",
      "entityId": "01HXYZ123456789ABCDEFGI",
      "source": "slack",
      "eventType": "message",
      "summary": "Customer asked about pricing in #acme-corp",
      "metadata": {},
      "timestamp": "2026-08-01T00:00:00Z",
      "createdAt": "2026-08-01T00:00:00Z"
    }
  ],
  "pagination": {
    "total": 150,
    "limit": 50,
    "offset": 0,
    "totalIsLowerBound": true
  }
}

Cross-entity event search

Parameters
sourceFilter by source type (e.g.
eventTypeFilter by event type (e.g.
sinceFilter events after this ISO date.
untilFilter events before this ISO date.
limit50Maximum number of events to return (max 200).
offset0Number of events to skip for pagination.
curl -s "$ACKDB_URL/entities/events?limit=50&offset=0" \
  -H "Authorization: Bearer $ACKDB_API_KEY"
Response · 200
{
  "data": [
    {
      "id": "01HXYZ123456789ABCDEFGH",
      "entityId": "01HXYZ123456789ABCDEFGI",
      "entityName": "acme.com",
      "source": "stripe",
      "eventType": "stripe_charge.refunded",
      "summary": "Stripe refund: $50.00 USD",
      "metadata": {
        "refund_amount_cents": 5000,
        "currency": "usd",
        "refund_full": true
      },
      "timestamp": "2026-02-15T14:30:00.000Z",
      "createdAt": "2026-02-15T14:30:01.000Z"
    }
  ],
  "pagination": {
    "total": 1,
    "limit": 50,
    "offset": 0
  }
}

Counts and sums across every company, no rows retrieved.

Parameters
sourcestripeScope the rollup to one source.
sumFieldamount_paid_centsA promoted metadata field. It unlocks sum, avg, min and max.
groupBymonthevent_type, source, month or entity. minCount and minSum act as HAVING.
curl -s "$ACKDB_URL/entities/events/aggregate?source=stripe&sumField=amount_paid_cents&groupBy=month" \
  -H "Authorization: Bearer $ACKDB_API_KEY"
Response · 200
{
  "data": {
    "count": 45,
    "distinctEntities": 12,
    "sum": 1250000,
    "avg": 27777.78,
    "min": 5000,
    "max": 99000,
    "groups": [
      {
        "key": "stripe_charge.succeeded",
        "count": 30,
        "distinctEntities": 10,
        "sum": 0,
        "avg": 0,
        "min": 0,
        "max": 0
      }
    ]
  }
}

Find cross-source temporal event sequences

Parameters
firstEventTyperequiredThe event type that happens first (e.g., 'stripe_charge.refunded').
secondEventTyperequiredThe event type that happens after (e.g., 'slack_message').
windowDays7Maximum days between events (default 7).
firstSourceFilter first event by source.
secondSourceFilter second event by source.
sinceOnly consider first events on or after this date.
untilOnly consider second events on or before this date.
limit50Maximum results to return.
offset0Number of results to skip for pagination.
curl -s "$ACKDB_URL/entities/events/sequence?windowDays=7&limit=50&offset=0" \
  -H "Authorization: Bearer $ACKDB_API_KEY"
Response · 200
{
  "data": [
    {
      "entityId": "…",
      "entityName": "…",
      "daysBetween": 0,
      "firstEvent": {
        "id": "01HXYZ123456789ABCDEFGH",
        "entityId": "01HXYZ123456789ABCDEFGI",
        "source": "slack",
        "eventType": "message",
        "summary": "Customer asked about pricing in #acme-corp",
        "metadata": {},
        "timestamp": "2026-08-01T00:00:00Z",
        "createdAt": "2026-08-01T00:00:00Z"
      },
      "secondEvent": {
        "id": "01HXYZ123456789ABCDEFGH",
        "entityId": "01HXYZ123456789ABCDEFGI",
        "source": "slack",
        "eventType": "message",
        "summary": "Customer asked about pricing in #acme-corp",
        "metadata": {},
        "timestamp": "2026-08-01T00:00:00Z",
        "createdAt": "2026-08-01T00:00:00Z"
      }
    }
  ],
  "pagination": {
    "total": 150,
    "limit": 50,
    "offset": 0,
    "totalIsLowerBound": true
  }
}

Event type params take the bare name (invoice.paid); stored names are source-prefixed. On the timeline, source= / sources= / excludeSources= are mutually exclusive; excludeSources=system,custom,member hides bookkeeping.

People

A person is the human; a contact is that human at one company.

List a company's people

Parameters
entityIdrequiredCompany id.
statusactiveWhich affiliations to return.
is_externaltrue = customer-side contacts only; false = internal staff only.
curl -s "$ACKDB_URL/entities/{entityId}/contacts?status=active" \
  -H "Authorization: Bearer $ACKDB_API_KEY"
Response · 200
{
  "data": "…"
}

Fuzzy contact search → the single closest contact (cross-entity)

Parameters
qMatch across all searchable fields (name/email/phone/title/department/linkedin).
nameMatch the contact name.
emailMatch the contact email.
phoneMatch the contact phone.
titleMatch the job title.
departmentMatch the department.
linkedinMatch the LinkedIn URL.
curl -s "$ACKDB_URL/contacts/search" \
  -H "Authorization: Bearer $ACKDB_API_KEY"
Response · 200
{
  "data": "…"
}

Find the single closest person (per-field)

Parameters
qMatch across the name and all key values.
name
email
linkedin
identityMatch any durable key value (product ids included).
identityTypePin identity to one key type, e.g.
contactLimit25
eventLimit10
curl -s "$ACKDB_URL/persons/search?contactLimit=25&eventLimit=10" \
  -H "Authorization: Bearer $ACKDB_API_KEY"
Response · 200
{
  "data": "…"
}

Person profile

Parameters
personIdrequiredPerson id (ULID = persons.person_id).
contactLimit25
eventLimit20
curl -s "$ACKDB_URL/persons/{personId}?contactLimit=25&eventLimit=20" \
  -H "Authorization: Bearer $ACKDB_API_KEY"
Response · 200
{
  "data": "…"
}

The person timeline (cross-company)

Parameters
personIdrequiredPerson id (ULID = persons.person_id).
limit20
offset0
source
sourcesCSV include list — only these sources.
excludeSourcesCSV exclude list — drop these sources (e.g.
eventType
since
until
curl -s "$ACKDB_URL/persons/{personId}/events?limit=20&offset=0" \
  -H "Authorization: Bearer $ACKDB_API_KEY"
Response · 200
{
  "data": "…"
}

The person timeline is what answers "have we talked to them before?" — contact at a previous employer still counts, and only that timeline shows it.

Content & evidence

Semantic search over entity content

Parameters
entityIdrequiredEntity ID (ULID)
qrequiredNatural language search query (e.g., 'pricing discussions', 'payment failures')
limit10Maximum number of results to return.
curl -s "$ACKDB_URL/entities/{entityId}/search?limit=10" \
  -H "Authorization: Bearer $ACKDB_API_KEY"
Response · 200
{
  "data": [
    {
      "source": "…",
      "text": "…",
      "score": 0,
      "timestamp": "2026-08-01T00:00:00Z",
      "metadata": {}
    }
  ],
  "query": "…",
  "total": 0
}

List or search entity conversations

Parameters
entityIdrequired
qSearch query for semantic search
sourceFilter by source
topicFilter by topic
sentimentFilter by sentiment
hasActionItemsFilter to conversations with action items
since
until
limit20
offset0
curl -s "$ACKDB_URL/entities/{entityId}/conversations?limit=20&offset=0" \
  -H "Authorization: Bearer $ACKDB_API_KEY"
Response · 200
{
  "data": [
    {
      "id": "…",
      "entityId": "…",
      "source": "slack",
      "sourceThreadKey": "…",
      "messageCount": 0,
      "participantNames": [
        "…"
      ],
      "hasExternal": true,
      "sourceMetadata": {},
      "summary": "…",
      "topics": [
        "…"
      ],
      "sentiment": "positive",
      "actionItems": [
        "…"
      ],
      "fileIds": [
        "…"
      ],
      "hasFiles": true
    }
  ],
  "pagination": {
    "total": 0,
    "limit": 0,
    "offset": 0
  }
}

Get raw payloads for an entity

Parameters
entityIdrequiredEntity ID (ULID)
limit20Maximum number of raw payloads to return.
offset0Number of raw payloads to skip for pagination.
curl -s "$ACKDB_URL/entities/{entityId}/raw?limit=20&offset=0" \
  -H "Authorization: Bearer $ACKDB_API_KEY"
Response · 200
{
  "data": [
    {
      "id": "01HXYZ123456789ABCDEFGH",
      "source": "slack",
      "payload": {},
      "createdAt": "2026-08-01T00:00:00Z"
    }
  ],
  "pagination": {
    "total": 150,
    "limit": 50,
    "offset": 0,
    "totalIsLowerBound": true
  }
}

List files for an entity

Parameters
entityIdrequiredEntity ID
sourceFilter by source
fileTypeFilter by MIME type prefix (e.g., 'image/', 'application/pdf')
processedFilter by processing status
limit50Max results
offset0Pagination offset
curl -s "$ACKDB_URL/entities/{entityId}/files?limit=50&offset=0" \
  -H "Authorization: Bearer $ACKDB_API_KEY"
Response · 200
{
  "data": [
    {
      "id": "…",
      "entityId": "…",
      "eventId": "…",
      "source": "slack",
      "fileName": "q4-proposal.pdf",
      "fileType": "application/pdf",
      "fileSizeBytes": 2048576,
      "sourceFileId": "…",
      "sourceUrl": "…",
      "metadata": {},
      "description": "…",
      "processed": true,
      "createdAt": "2026-08-01T00:00:00Z"
    }
  ],
  "pagination": {
    "total": 0,
    "limit": 0,
    "offset": 0
  }
}

Get file metadata

Parameters
fileIdrequiredFile ID
curl -s "$ACKDB_URL/files/{fileId}" \
  -H "Authorization: Bearer $ACKDB_API_KEY"
Response · 200
{
  "data": {
    "id": "…",
    "entityId": "…",
    "eventId": "…",
    "source": "…",
    "fileName": "…",
    "fileType": "…",
    "fileSizeBytes": 0,
    "sourceFileId": "…",
    "sourceUrl": "…",
    "metadata": {},
    "description": "…",
    "extractedText": "…",
    "contentChunkId": "…",
    "processed": true
  }
}

Download file content

Parameters
fileIdrequiredFile ID
curl -s "$ACKDB_URL/files/{fileId}/content" \
  -H "Authorization: Bearer $ACKDB_API_KEY"

Get a company logo (public)

Parameters
idrequiredEntity id (ULID).
curl -s "$ACKDB_URL/entities/{id}/logo" \
  -H "Authorization: Bearer $ACKDB_API_KEY"

Semantic search answers 501 until an embedding provider is configured. q= on conversations switches list mode to search mode. More: Search and files.

Tables

The console's column surface — every computed column (evc__ counts, emf__ fields, sc__ scores, account_owner), the same grammar segments save:

List Observe company fields

curl -s "$ACKDB_URL/observe/companies/fields" \
  -H "Authorization: Bearer $ACKDB_API_KEY"
Response · 200
{
  "data": [
    {
      "key": "event_count",
      "label": "Event Count",
      "group": "system",
      "valueType": "string",
      "source": "system",
      "coverage": 1280,
      "sortable": true,
      "filterable": true,
      "operators": [
        "…"
      ],
      "allowedValues": [
        "…"
      ],
      "distinctValuesUrl": "/observe/companies/fields/hubspot_lifecycle_stage/values"
    }
  ]
}

Query companies

Body
{
  "filter": {
    "type": "group",
    "op": "and",
    "children": [
      {
        "type": "group",
        "op": "and",
        "children": [
          {}
        ]
      }
    ]
  },
  "sort": [
    {
      "field": "event_count",
      "dir": "asc"
    }
  ],
  "columns": [
    {
      "field": "name",
      "frozen": true,
      "width": 200
    }
  ],
  "limit": 50,
  "offset": 0,
  "ids": [
    "…"
  ]
}
curl -s -X POST "$ACKDB_URL/observe/companies/query" \
  -H "Authorization: Bearer $ACKDB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "filter": { "type": "group", "op": "and", "children": [ { "type": "group", "op": "and", "children": [ {} ] } ] }, "sort": [ { "field": "event_count", "dir": "asc" } ], "columns": [ { "field": "name", "frozen": true, "width": 200 } ], "limit": 50, "offset": 0, "ids": [ "…" ] }'
Response · 200
{
  "data": [
    {
      "entityId": "…",
      "name": "…",
      "domain": "…"
    }
  ],
  "pagination": {
    "total": 150,
    "limit": 50,
    "offset": 0,
    "totalIsLowerBound": true
  }
}

Export companies as CSV

Body
{
  "filter": {
    "type": "group",
    "op": "and",
    "children": [
      {
        "type": "group",
        "op": "and",
        "children": [
          {}
        ]
      }
    ]
  },
  "sort": [
    {
      "field": "event_count",
      "dir": "asc"
    }
  ],
  "columns": [
    {
      "field": "name",
      "frozen": true,
      "width": 200
    }
  ],
  "limit": 50,
  "offset": 0,
  "ids": [
    "…"
  ]
}
curl -s -X POST "$ACKDB_URL/observe/companies/export" \
  -H "Authorization: Bearer $ACKDB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "filter": { "type": "group", "op": "and", "children": [ { "type": "group", "op": "and", "children": [ {} ] } ] }, "sort": [ { "field": "event_count", "dir": "asc" } ], "columns": [ { "field": "name", "frozen": true, "width": 200 } ], "limit": 50, "offset": 0, "ids": [ "…" ] }'

Contacts and persons have twin surfaces under /observe/contacts/* and /observe/persons/*.

Ask

Ask a question (built-in AI agent)

Body
{
  "question": "What companies do we have data on?",
  "sessionId": "…",
  "entityId": "…"
}
curl -s -X POST "$ACKDB_URL/ask" \
  -H "Authorization: Bearer $ACKDB_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "question": "What companies do we have data on?", "sessionId": "…", "entityId": "…" }'
Response · 200
{
  "answer": "…",
  "citations": [
    {
      "claim": "…",
      "source": "…",
      "eventType": "…",
      "timestamp": "…"
    }
  ],
  "steps": [
    {
      "type": "thinking",
      "content": "…",
      "toolName": "…",
      "durationMs": 0
    }
  ],
  "toolCallCount": 0,
  "totalDurationMs": 0,
  "sessionId": "…"
}

/mcp serves the same tools to your own AI client: Connect your AI client.