Skip to content

Contact Memory and Correspondence

Inkbox can derive concise memories from a contact's email, SMS, iMessage, and call history. Every derived memory includes one or more citations to the source correspondence, and you can add or correct memories by hand.

Contact list, create, update, and merge responses include memory_count, the number of memories currently in effect for that contact.

Fact text is organization-wide. For an identity-scoped caller, citations owned by that identity include their source ID, locator, and resolver URL; citations owned by other identities remain in the response but redact those source details. Organization-wide authenticated callers can inspect correspondence across the organization. Unavailable or inaccessible citations report only their source type and availability.

List correspondence GET

GET /contacts/{contact_id}/correspondence

Returns correspondence between one contact and one agent identity across supported channels.

ParameterTypeDefaultDescription
identity_idUUIDCaller identityRequired for organization-wide callers; scoped callers use their own identity.
channelsstring[]AllRepeat or comma-separate email, sms, imessage, and calls.
afterdatetimeNoneInclude items at or after this time.
beforedatetimeNoneInclude items before this time.
limit_per_channelinteger25Default result limit for each selected channel.
email_limit, sms_limit, imessage_limit, calls_limitintegerNoneOverride the limit for one channel.
cursorstringNoneCursor returned by the previous response.
orderstringdescasc or desc.
contentstringpreviewmetadata, preview, or full.
transcriptsstringnonenone, abridged, or full for calls.
include_failedbooleanfalseInclude failed correspondence.

Memory kinds

Every memory carries a kind saying what sort of thing it records:

KindHoldsExpires
profileWho someone is: their role, company, location, or household.Never
preferenceA standing instruction for dealing with them: a preferred channel, a schedule, a tone.Never
contextA live situation with a natural end: a current project, a trip, an open deal.30 days after it was last mentioned

A contact holds up to 10 active memories of each kind. The three kind budgets are independent, so a contact already full on profile can still take a new preference or context memory.

Memories Inkbox derives for you are also held to 30 active memories across the whole contact, and memories with kind: null count toward that total. Memories you add or edit yourself are held only to the 10-per-kind limit for the kind you choose, so a contact carrying memories recorded before kinds existed can end up above 30.

A context memory carries an expires_at timestamp 30 days out. Each time the same situation comes up again in correspondence that timestamp moves forward, so a live project keeps its place while one that goes quiet drops out and frees its slot for something current. profile and preference memories report expires_at: null, as do memories you write or edit yourself, which are never rewritten or expired automatically whatever their kind.

Once expires_at passes, an unlocked memory stops being returned, stops counting toward memory_count, and releases its context slot. A memory with locked_at remains active after that timestamp: it stays in the default list and count and continues using its slot. Expiry is not deletion: pass include_expired=true when listing to see expired memories.

List memories GET

GET /contacts/{contact_id}/facts

Returns the contact's memories in most-recently-updated order.

ParameterTypeDefaultDescription
include_expiredbooleanfalseInclude expired context memories. Locked memories remain active and are returned without this option.

Each memory contains:

FieldTypeDescription
idUUIDMemory ID.
contact_idUUIDContact the memory belongs to.
contentstringThe memory text.
kindstring | nullprofile, preference, or context. null on memories recorded before kinds existed.
confidencenumber | nullExtraction confidence between 0 and 1, on derived memories.
originstringgenerated for a memory Inkbox derived, user for one you wrote or edited.
expires_atdatetime | nullWhen an unlocked generated context memory stops applying; null when it has no deadline.
locked_atdatetime | nullSet when the memory is protected from automatic changes. A locked memory remains active even after expires_at.
created_atdatetimeWhen the memory was first recorded.
updated_atdatetimeWhen the memory was last changed.
citationsobject[]Sources supporting the memory.

Get a memory GET

GET /contacts/{contact_id}/facts/{fact_id}

Returns one memory with its citations under the same information-flow boundary described above. A memory ID stays readable once its deadline has passed, so this call returns expired memories without include_expired.

Add a memory POST

POST /contacts/{contact_id}/facts

Records a memory you write yourself. This requires an admin-scoped API key or an authenticated Console user; an agent-scoped key cannot write memories.

FieldTypeRequiredDescription
contentstringYesThe memory text, up to 500 characters.
kindstringYesprofile, preference, or context.
JSONJSON

The memory is created with origin: "user", no citations, and expires_at: null.

Returns 409 when the contact already holds 10 active memories of that kind. Adding a memory never evicts an existing one; delete one first, then retry.

Edit a memory PATCH

PATCH /contacts/{contact_id}/facts/{fact_id}

Corrects the wording of a memory, moves it to a different kind, or both. An expired memory can be edited by ID. Same access as adding one: an admin-scoped API key or an authenticated Console user.

FieldTypeRequiredDescription
contentstringNoReplacement text, up to 500 characters.
kindstringNoprofile, preference, or context.

Send at least one of the two. Neither accepts null, and an omitted field keeps its stored value.

JSONJSON

Changing content drops the memory's confidence and its citations, since that evidence supported the previous wording rather than yours. Changing kind alone leaves both in place. Any successful edit makes the memory origin: "user", clears expires_at, and revives it if it had expired.

Moving a memory into context leaves expires_at as null, because a memory you curate stays until you remove it. Returns 409 when retyping or reviving the memory would put it into a kind that is already full.

Delete a memory DELETE

DELETE /contacts/{contact_id}/facts/{fact_id}

Deletes one active or expired memory. This requires an admin-scoped API key or an authenticated Console user.

The response identifies the deleted fact and returns the contact's remaining memory state:

JSONJSON

latest_memory is null when no active memories remain.

Resolve a citation GET

GET /contacts/{contact_id}/facts/{fact_id}/citations/{citation_id}

Returns the source ID and locator after rechecking that the caller can still access the underlying correspondence. Returns 404 when the citation or its source is unavailable.

Merge duplicate contacts POST

POST /contacts/{contact_id}/merge

Merges the contacts in losing_contact_ids into the contact named in the path. The survivor retains the combined identifiers, correspondence, and memories; losing contacts become merged records. This operation requires an admin-scoped API key or authenticated Console user.

JSONJSON

field_sources is optional. It selects which merged contact supplies each scalar profile field. Each value must be the survivor ID or one of the losing_contact_ids. Allowed keys are preferred_name, name_prefix, given_name, middle_name, family_name, name_suffix, company_name, job_title, birthday, and notes.

A merge holds the surviving contact to 10 active memories of each kind and 30 active memories in total, with kind: null memories counting toward the total. If the combined contacts would exceed a kind or total limit, the entire merge is rejected without changing any contact. The error names each overflowing kind or total: delete a memory from each named kind, or any active memory for total, then retry.

The response is the surviving contact and includes its current memory_count and latest_memory.