--- title: "Projects, data points, memories, audiences, identities and traits" description: "The five nouns of every call, the memories that recall returns, and every limit in one table." canonical: https://past.dev/docs/memory-api/concepts last-updated: 2026-10-26 --- # Projects, data points, memories, audiences, identities and traits > The five nouns of every call, the memories that recall returns, and every limit in one table. Product: past.dev Memory API. Source: https://past.dev/docs/memory-api/concepts A data point is what you send: one piece of text with the time it happened. A memory is a short statement past.dev derives from data points, such as an event, a fact, a rule or a date, kept with the excerpt that supports it. The Memory section below defines the seven kinds of memory. A document is what a recall returns: a data point or a memory. Each result is one document with `artifact` context and `sources` evidence. Each source group carries your `sourceId`, its date, the `label` and `metadata` you sent when you sent them, and verbatim quotation strings in `excerpts`. `artifact.id` identifies the underlying memory; several results can refer to the same artifact. A project holds all of them. An audience decides who can read a data point, an identity is the reader a recall answers as, and traits describe an identity. ### Project A project is the isolation boundary. A project key selects one project, so `projectId` is never sent in a request body. Data points, memory, audiences, identities and keys belong to exactly one project, and nothing crosses between projects. The console shows the current project in its header. Every project has one audience with the slug `project`. It reaches every identity of the project, and it is the scope of a data point sent without `audience`. Projects are created in the console or with `POST /api/v1/projects` and the management key. ### Data point A data point is one thing that happened, sent as raw text with the time it happened. It is the unit of replacement and of deletion. [Sending data](/docs/memory-api/sending-data) says how to cut a source into data points. | Field | What it is for | | --- | --- | | `id` (string) | Your stable id for this data point, unique in the project. Every response returns it as `sourceId`. A second send with the same id whose content, label, metadata and timestamp all match what is stored changes nothing. A change to any of the four replaces the data point and the memory derived from it. Supply it: when omitted, a hash of `content` is used, so a corrected text becomes a second data point. | | `content` (string, required) | The raw text, complete and unedited. Empty or whitespace-only content is refused. | | `timestamp` (ISO 8601) | When the thing happened. Every response returns it as `occurredAt`. When omitted, an existing id keeps its stored time and a new data point takes the send time. | | `label` (string) | A short title for the data point, such as `Email from Nadia`. Memory extraction reads it beside the content, and recall returns it on the data point's sources. Omission retains the stored value for an existing id. | | `metadata` (JSON) | Your own JSON, stored with the data point. Recall never filters on it. | | `audience` (string) | The slug of the audience that can read this data point. Omit it, or send `project`, for a data point every identity can read. | | `identity` (string) | Your id for the author of this data point. It creates the identity record on first sight and does not scope the data point. Set it only when the author is also a reader you want in the directory: every new value creates a record. The sample below sets it because Dana reads memory. | ```json { "id": "crm-note-8841", "content": "Renewal call with Acme. Sam asked for per-seat billing and a quote for 40 seats before month end.", "label": "Renewal call", "timestamp": "2026-04-03T14:00:00Z", "audience": "acme", "identity": "dana@example.com" } ``` ### Memory A memory is a short statement that past.dev derives from the text of a data point. It keeps the excerpt that supports it, the time of the data point, and the speaker when the text names one. A recall returns memories and data points as documents. `artifact.kind` on each result says what the document is, and `content` holds the statement. | Kind | What it holds | | --- | --- | | `source` | A data point that you sent. `content` is its text as you sent it. | | `observation` | Something that happened or is still open, in the words of the data point. Example: `Nadia: Acme moved the pilot to Q4 (decision).` | | `claim` | One property of one thing and its value: an amount, a count, a size, a setting or the scheduled time of an event. A later value for the same property can supersede the earlier claim, which then carries `artifact.supersededAt`. Example: `Acme pilot budget: 40k.` | | `rule` | An instruction, a preference or a policy. `content` also says how firmly it applies and to what. Example: `Invoices go out on the first business day (required, standing). Applies to billing.` | | `membership` | One member of a named set. Example: `Pilot customers: Acme (member).` | | `identity` | Two names for one thing, or a rename. Example: `Project Kestrel was renamed Heron.` This kind has no relation to the identities that read memory. | | `mention` | A subject that the data point raises, such as a question or a request, as a short label. The detail is in an `observation` or in the data point. Example: `Dana: Asked about export limits.` | | `temporal-anchor` | A time expression and the event that it dates, such as a deadline or a duration. When the date can be calculated, `content` gives it. Example: `Pilot review (deadline): end of March. Resolved to 2026-03-31.` | `content` starts with the speaker when the data point names one, as in `Nadia: ...`. A statement that the source does not give as fact also carries its status: `hypothetical` for a condition or a guess, `proposed` for an option that nobody adopted, and `retracted` for a statement that the source withdrew, as in `Nadia (proposed): ...`. Read the status before you use a memory as a fact. The list of kinds can grow. Treat an unknown `artifact.kind` as a memory. `GET /api/v1/trace/nodes/{id}` returns the same value in `memoryKind`. ### Audience An audience is a named scope inside a project. Its slug, a short lowercase name used in URLs, is what a data point names in `audience`, and what a recall reaches through the identity. A slug has 2 to 40 characters: lowercase letters, digits and dashes, with a letter or digit at each end. Audiences are created on the Audiences screen of the console or with `PUT /api/v1/audiences/{slug}`. A data point that names a slug that does not exist is refused with `400 audience-unknown`. An audience you create has one of two kinds. A `fixed` audience lists the identity ids it reaches. A `rule` audience reaches every identity whose traits match its rule: `match` is `all` or `any`, over 1 to 20 conditions of the form trait, operator, values. The default audience `project` reports `kind` `default` on the wire. It reaches every identity, and it cannot be replaced or deleted. An audience whose list or rule names nobody holds memory that nobody can read until it names someone again. The memory of a deleted audience stays unreadable for good; the slug is free, and a new audience under that slug does not reach it. Deleting an audience erases nothing and gives its memory to no other audience: to erase that memory, delete its data points with `POST /api/v1/data-points/delete`. A change to a list, a rule or a trait applies to recall within a short window of seconds, and `POST /api/v1/audiences/{slug}/recompute` applies it now. A memory is never readable by more identities than the data points behind it: a reader must be able to read every data point a memory rests on. ```json { "kind": "rule", "name": "Acme", "rule": { "match": "all", "conditions": [ { "trait": "company", "operator": "is", "values": ["acme"] } ] } } ``` ### Identity An identity is your own id for a reader: a person, an agent or a service that calls recall. The id has 1 to 200 characters and is never generated by past.dev. Use a stable identifier your application already owns, such as an email address or a Slack user id. The same id is what every recall for that reader answers as. An identity record is created the first time the id is seen: in `identity` on a data point, in the list of a `fixed` audience, or through `PUT /api/v1/identities/{identity}`. A recall for an id that has no record reads project-visible memory only, and it never creates the record. `DELETE /api/v1/identities/{identity}` removes the record and its audience memberships. The memory stays. The Audiences screen of the console lists identities on its Identities tab. `origin` records how a record was created and never changes: `api` through the identity routes, `ingest` first seen on a data point, `audience` first named in a fixed list, `csv` a CSV import in the console, `console` created by hand in the console, `connector` a connected identity source, `login` a sign-in through the project's MCP server, `link` an access link issued to an agent. `lastSeenAt` is the time of the last data point or recall that carried the id, null until one does. ```json { "traits": { "company": "acme", "team": "sales" } } ``` ### Trait A trait is a string attribute of an identity: a key of 1 to 200 characters with one string value of up to 2,000 characters. Traits are written through the identity routes and the console, and audience rules match on them. `is`, `is_not`, `is_one_of`, `is_not_one_of` and `exists` match only an identity that has the trait; `does_not_exist` matches an identity without it, including one with no traits at all. - `is` and `is_not` take exactly one value. - `is_one_of` and `is_not_one_of` take 1 to 50 values. - `exists` and `does_not_exist` take no value. - `is_not` and `is_not_one_of` match only an identity that has the trait with another value. ### Metadata Metadata is your own JSON, stored with the data point. Recall has no metadata filter. Use an audience to decide who reads a data point, and use the identity to decide what a read reaches. When a re-send with the same `id` omits `metadata`, the stored value is kept. No key is reserved, and the platform parses none: metadata is stored and returned exactly as sent. Memory extraction reads it whole beside the content, as descriptive context, so keys such as `author`, `role`, `conversationId`, `sessionId`, `messageId` or `ordinal` help it attribute and order what it reads. Use whatever names your data already has. ### What recall returns for an identity A recall names the identity it answers as. The read returns project-visible memory plus the memory of every audience the identity reaches, and nothing else. An identity reaches a `fixed` audience that lists it and every `rule` audience its traits match. `GET /api/v1/identities/{identity}` returns the slugs the identity reaches at that moment. A membership change reaches recall after a short window, so a read made in the seconds after the change can still use the previous membership. An id with no record reads project-visible memory only and raises no error, so validate the id on your side before the call. ### Limits | Limit | Value | | --- | --- | | Data points per batch | 1 to 1,000 | | Content per request | 16 MiB of UTF-8, single sends included | | Data point cut from a conversation | Under 16,000 characters per part; a longer source is split into parts, see Sending data | | Data point cut from a document | Under 32,000 characters per part | | Source id | Any non-blank string, trimmed; no length limit | | Identity id | 1 to 200 characters | | Trait key and value | Key 1 to 200 characters, value 1 to 2,000, one string per key | | Audience slug | 2 to 40 lowercase letters, digits and dashes, a letter or digit at each end | | Audience name | 1 to 200 characters | | Fixed audience list | No limit | | Rule | 1 to 20 conditions; 1 to 50 values for `is_one_of` and `is_not_one_of` | | Identities per bulk call | 1 to 1,000 | | Ids per delete call | 1 to 1,000 | | Recall `limit` | 1 to 200 documents, default 100. The page ends at `limit` or at the evidence budget, whichever comes first. | | Recall `level` and `maxTokens` | `low` 8,000 tokens by default, `medium` 16,000, `high` 24,000, `extra-high` 32,000, or a `maxTokens` figure; a token is about four characters. A budget above the plan's ceiling is lowered to it: 8,000 on Pay as you go and Flex, 32,000 on Enterprise. | | Identity list page | 1 to 1,000, default 100 | | Idempotency key | Up to 200 characters, no expiry | | Project name | Up to 255 characters, at least two letters or digits | | Key name | 1 to 64 characters | | Credits | One per 350 bytes of content, at least one per data point; prices on the pricing page | | Requests per minute | Per key: 500 on Pay as you go, 750 on Flex, 1,000 on Enterprise | | Webhooks per project | 20 | | Webhook name | 1 to 64 characters | | Webhook event body | 1 MiB | | Webhook events per minute | 600 for each webhook, on every plan | | Webhook filters | Up to 20; 1 to 50 values for `is any of` | | Webhook path | Up to 500 characters | | Projects per organization | 1 on Pay as you go, 5 on Flex, unlimited on Enterprise, archived projects included | | Request log payload retention | 7 days on Pay as you go, 90 days on Flex, unlimited on Enterprise. The record of the call stays after its payload expires |