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
Returns correspondence between one contact and one agent identity across supported channels.
| Parameter | Type | Default | Description |
|---|---|---|---|
identity_id | UUID | Caller identity | Required for organization-wide callers; scoped callers use their own identity. |
channels | string[] | All | Repeat or comma-separate email, sms, imessage, and calls. |
after | datetime | None | Include items at or after this time. |
before | datetime | None | Include items before this time. |
limit_per_channel | integer | 25 | Default result limit for each selected channel. |
email_limit, sms_limit, imessage_limit, calls_limit | integer | None | Override the limit for one channel. |
cursor | string | None | Cursor returned by the previous response. |
order | string | desc | asc or desc. |
content | string | preview | metadata, preview, or full. |
transcripts | string | none | none, abridged, or full for calls. |
include_failed | boolean | false | Include failed correspondence. |
Memory kinds
Every memory carries a kind saying what sort of thing it records:
| Kind | Holds | Expires |
|---|---|---|
profile | Who someone is: their role, company, location, or household. | Never |
preference | A standing instruction for dealing with them: a preferred channel, a schedule, a tone. | Never |
context | A 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
Returns the contact's memories in most-recently-updated order.
| Parameter | Type | Default | Description |
|---|---|---|---|
include_expired | boolean | false | Include expired context memories. Locked memories remain active and are returned without this option. |
Each memory contains:
| Field | Type | Description |
|---|---|---|
id | UUID | Memory ID. |
contact_id | UUID | Contact the memory belongs to. |
content | string | The memory text. |
kind | string | null | profile, preference, or context. null on memories recorded before kinds existed. |
confidence | number | null | Extraction confidence between 0 and 1, on derived memories. |
origin | string | generated for a memory Inkbox derived, user for one you wrote or edited. |
expires_at | datetime | null | When an unlocked generated context memory stops applying; null when it has no deadline. |
locked_at | datetime | null | Set when the memory is protected from automatic changes. A locked memory remains active even after expires_at. |
created_at | datetime | When the memory was first recorded. |
updated_at | datetime | When the memory was last changed. |
citations | object[] | Sources supporting the memory. |
Get a memory GET
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
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.
| Field | Type | Required | Description |
|---|---|---|---|
content | string | Yes | The memory text, up to 500 characters. |
kind | string | Yes | profile, preference, or context. |
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
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.
| Field | Type | Required | Description |
|---|---|---|---|
content | string | No | Replacement text, up to 500 characters. |
kind | string | No | profile, preference, or context. |
Send at least one of the two. Neither accepts null, and an omitted field keeps its stored value.
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
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:
latest_memory is null when no active memories remain.
Resolve a citation GET
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
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.
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.