AckdbIngestRequest
Request body for data ingestion from workflow tools. Content is validated against the source's content_schema, traits against trait_definitions. Ownership comes from the event type: entity is company-only, person is a person at a company, person_only is the human alone. Personal requests omit company links (or send empty arrays), forbid non-null entityId/contactId and nonempty contacts/traits/files, and resolve a durable person key or explicit personId. Personal structured/activity events create no affiliations.
Properties
sourcestringrequiredSource type (must exist in schema registry).
contentobjectrequiredThe content payload with text and source-specific metadata.
textstringrequiredRaw text from the source - this is what gets embedded and searched.
metadataobjectrequiredSource-specific fields, validated against the source's content_schema.
identityLinksarray ofAt least one company link for entity/person types; omit or empty for person_only.
traitsarray ofPre-computed trait values. Each is validated against trait_definitions.
entityIdstring | nullExplicit entity ID. If provided and no identity match, uses this entity.
contactIdstring | nullOptional strict affiliation pin for company-associated person events. Omit or null for person_only.
personIdstringOptional strict human ULID pin. Personal events create no affiliation. Existing person-scope events retain company affiliation resolution.
timestampstring <date-time>requiredWhen this data was created in the source system (not when ingested).
idempotencyKeystringOptional caller-provided idempotency key. If omitted, ackDB auto-computes one via SHA-256(source + content). Duplicate keys are silently accepted (200 with deduplicated: true). Use this to pass a natural key like a Stripe event ID or Slack message timestamp.
contactsarray ofOptional people to create or update at this entity, in addition to whatever the event's contact mappings extract. This is how a multi-person event (a meeting's attendee list, a group thread) records everyone: the contactId/personId rails above pin ONE actor, this array carries the rest. Each entry resolves independently through the same person-first resolver.
filesarray ofOptional files to upload and attach to this entity. Each file is base64-encoded. Max 10MB per file (configurable via FILE_MAX_SIZE_BYTES). Files are stored in the configured FileStore and processed asynchronously by `npm run process-files` to generate embeddings for semantic search.
Example
{
"source": "slack",
"content": {
"text": "Customer asked about pricing",
"metadata": {}
},
"identityLinks": [
{
"externalIdType": "domain",
"externalIdValue": "C04ABC",
"confidence": 1
}
],
"traits": [
{
"traitKey": "slack_message_count",
"value": 42,
"op": "increment",
"workflow": "slack-ingestion",
"confidence": 1
}
],
"entityId": null,
"contactId": "01KWSVN7YY7Q2Q9QZ8GEHHYW14",
"personId": "01KWSVN7YY7Q2Q9QZ8GEHHYW14",
"timestamp": "2026-03-08T10:00:00Z",
"idempotencyKey": "stripe:evt_1234567890",
"contacts": [
{
"contactId": "01JQC7X8Y9Z0A1B2C3D4E5F6G7",
"email": "string",
"name": "string",
"title": "string",
"phone": "string",
"department": "string",
"linkedinUrl": "string",
"isExternal": false,
"identityLinks": [
{
"externalIdType": "slack_user_id",
"externalIdValue": "U07ABC123"
}
],
"traits": [
{
"$ref": "See AckdbIngestTrait"
}
]
}
],
"files": [
{
"name": "q4-proposal.pdf",
"type": "application/pdf",
"sizeBytes": 2048576,
"content": "string",
"sourceFileId": "F04ABC123",
"sourceUrl": "https://files.slack.com/files-pri/T04ABC-F04ABC123/q4-proposal.pdf",
"metadata": {}
}
]
}