--- title: "Set up an integration, step by step" description: "Every call in order, with the response value that lets you move to the next step." canonical: https://past.dev/docs/memory-api/setup last-updated: 2026-09-15 --- # Set up an integration, step by step > Every call in order, with the response value that lets you move to the next step. Product: past.dev Memory API. Source: https://past.dev/docs/memory-api/setup This page is the whole procedure, from access to production. Each step names the call it makes and the response value that gates the next step. Every request is complete and runs against a fresh project. ### 1. Get access and keys The console at https://app.past.dev is the web app where your team creates keys, audiences and identities by hand, imports identities from a CSV, and sees every call on the Requests screen. Sign in, pick the project in the header, create a project key on the API keys screen and store it as `PAST_API_KEY`. A script that creates projects or mints keys for several customers also needs the management key, created under Settings, Organization, Management key by an Owner or Admin, and stored as `PAST_MANAGEMENT_KEY`. ### 2. Decide the scope Two rules hold everywhere: the audience is the scope of a data point, and the identity is the reader of a recall. Choose one project per customer when customers must never share memory, or one project with one audience per customer when they share one body of memory. [Access control](/docs/memory-api/access-control) compares the two and shows the CRM and transcript patterns. The steps below run inside one project. ### 3. Create the audiences Create every audience a data point will name before the first send. A `fixed` audience lists identity ids. A `rule` audience matches identities by their traits. The response returns `slug` and `kind`; `201` means created and `200` means replaced. ```curl curl -X PUT https://api.past.dev/api/v1/audiences/acme \ -H "Authorization: Bearer $PAST_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "kind": "rule", "name": "Acme", "rule": { "match": "all", "conditions": [ { "trait": "company", "operator": "is", "values": ["acme"] } ] } }' curl -X PUT https://api.past.dev/api/v1/audiences/deal-4410 \ -H "Authorization: Bearer $PAST_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "kind": "fixed", "name": "Acme renewal 2026", "identities": ["dana@example.com", "lee@example.com"] }' ``` ### 4. Create the identities and their traits Create the readers with `POST /api/v1/identities/bulk`, up to 1,000 per call, with the traits your rule audiences match on. The response lists each identity with `created` or `updated`, and a trait change recomputes the rule audiences it affects. An identity first named in a `fixed` audience already exists with `origin` `audience`; one created here carries `origin` `api`. ```curl curl -X POST https://api.past.dev/api/v1/identities/bulk \ -H "Authorization: Bearer $PAST_API_KEY" \ -H "Content-Type: application/json" \ -d '[ { "identity": "dana@example.com", "traits": { "company": "acme", "team": "sales" } }, { "identity": "lee@example.com", "traits": { "company": "acme", "team": "support" } } ]' ``` ### 5. Cut the history into data points One data point is one thing that happened: one conversation for one day, one email, one record, one document, timed at its first message. Give each one a stable `id` from your source system, an explicit `timestamp` with a zone, and the `audience` that may read it. Keep a conversation part under 16,000 characters and a document part under 32,000. [Sending data](/docs/memory-api/sending-data) gives the unit for each source type and the shape of the text. ### 6. Send the history Send with `POST /api/v1/ingest/batch`: up to 1,000 data points and 16 MiB of content per call, with one `idempotencyKey` per batch, a name you choose so that a retry sends nothing twice. Batches may run in parallel, since every data point carries its own time; oldest first is a recommendation that keeps the history complete up to a date while the load runs. Keep the `ingestionId` of every batch and the ids you sent; no route lists them. A `202` means accepted. A `400` refuses the whole batch and names the item in `debugMessage`. ```curl curl -X POST https://api.past.dev/api/v1/ingest/batch \ -H "Authorization: Bearer $PAST_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "idempotencyKey": "crm-backfill-2026-04-0001", "items": [ { "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": "deal-4410" }, { "id": "crm-company-acme-2026-04-07", "content": "Acme moved its headquarters to Lyon. Ada is the new procurement contact.", "label": "Company update", "timestamp": "2026-04-07T10:00:00Z", "audience": "acme" } ] }' ``` ### 7. Wait for each batch Poll `GET /api/v1/ingest/{ingestionId}` every 2 seconds until `status` is `completed`; the other values are `processing` and `parked`. The time to complete depends on the size of the send, and `status` is the signal. Polls spend no credits and are outside the per-minute limit. `parked` means the send stopped and needs a person: write to support@past.dev with the `ingestionId` and the `X-Request-Id` header of the send. `settled`, `blocked` and the `readiness` counters describe every send in the project; [How it works](/docs/memory-api/how-it-works) defines them. ```curl curl https://api.past.dev/api/v1/ingest/7d9f2b6a-4c1e-4f8a-9b3d-2e5c8a1f6d40 \ -H "Authorization: Bearer $PAST_API_KEY" ``` ```200ok { "status": "completed", "settled": true, "blocked": false, "readiness": { "raw": 0, "comprehension": 0, "consolidation": 0, "parked": 0 } } ``` ### 8. Verify with a recall Recall as one identity of each scope and read the page. A correct setup returns a result whose `excerpts` carry a `sourceId` you sent. A recall as an identity outside the scope returns project-visible memory only; an empty page alone proves nothing, so confirm reach with `GET /api/v1/identities/{identity}`, which lists the audiences the identity reaches. ```curl curl -X POST https://api.past.dev/api/v1/recall \ -H "Authorization: Bearer $PAST_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "What did Acme ask for in the renewal?", "identity": "dana@example.com" }' # The same read as an identity outside both audiences returns project-visible memory only. curl -X POST https://api.past.dev/api/v1/recall \ -H "Authorization: Bearer $PAST_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "query": "What did Acme ask for in the renewal?", "identity": "visitor@example.com" }' ``` ### 9. Go live - Send new data points as they happen, with the same id scheme and `audience` on every one. - Correct a record with the same `id` and the new text, and send `audience` again: a re-send without it makes the data point project-visible. - Retract with `DELETE /api/v1/data-points/{sourceId}` or `POST /api/v1/data-points/delete`; the data points and the memory derived from them leave recall at once. - Retry a `429` with no body after a pause. Retry a `5xx` with the same `idempotencyKey`. Fix the request on any other `4xx`. - Find any call by its `X-Request-Id` on the Requests screen of the console, and quote that id to support@past.dev. [The Limits table on Concepts](/docs/memory-api/concepts#limits) holds every number this page uses.