# Set up an integration, step by step > Every call in order, with the response value that lets you move to the next step. ## 1. Get access and keys Call the API at `https://api.harny.ai/v1/memory`. Send an app key in the `Authorization: Bearer $HARNY_APP_KEY` header. App keys start with `fsa_`. Add `X-Harny-User: ` to name the person whose memory you read or write. A user key starts with `fsk_` and names its own user, so it does not need `X-Harny-User`. Read the key from the environment variable `HARNY_APP_KEY`. ```bash tab=cURL curl -X GET "https://api.harny.ai/v1/memory/usage" \ -H "Authorization: Bearer $HARNY_APP_KEY" ``` ```python tab=Python import os import requests headers = {"Authorization": f"Bearer {os.environ['HARNY_APP_KEY']}"} response = requests.get("https://api.harny.ai/v1/memory/usage", headers=headers) print(response.json()) ``` ```typescript tab=TypeScript const response = await fetch("https://api.harny.ai/v1/memory/usage", { headers: { Authorization: `Bearer ${process.env.HARNY_APP_KEY}` }, }); console.log(await response.json()); ``` The call returns `200` with the month's usage for the app. That response confirms the key. A wrong or missing key returns `401 unauthorized`. Every error uses the body `{"code", "status", "debugMessage", "data"}`, and every response carries `X-Request-Id`. [Errors](/docs/memory-api/errors) lists the codes. Move to step 2 when the usage call returns `200`. ## 2. Decide the scope Use one app per authorization domain. Each app key selects one app. Users are people. Each user has their own memory inside the app. The JSON field `projectId` in responses carries the app id. Move to step 3 when you know which apps you need and which users each one holds. ## 3. Group the users (optional) An audience is a named group of users. Create each one with `PUT /v1/memory/audiences/{slug}`. A `fixed` audience names the users it holds. A `rule` audience matches all or any of 1 to 20 conditions on user traits. Every app also has a default audience, `project`, that holds every user. Every data point goes to the default audience: the `audience` field takes only `project` or nothing. A group you create does not yet limit who reads a data point; to keep two customers apart, use one app for each. Skip this step if you do not need groups. Otherwise move to step 4 when `GET /v1/memory/audiences` lists every audience you created. ## 4. Create the users and their traits Create each user with `PUT /v1/memory/identities/{identity}`, or up to 1,000 at once with `POST /v1/memory/identities/bulk`. A user has an `identity`, `traits` (a map of strings) and `lastSeenAt`. A trait key is up to 200 characters and a trait value up to 2,000. When a call sends an `identity` in its body, it must equal the user the call acts as. Any other value returns `403 identity-mismatch`. Move to step 5 when `GET /v1/memory/identities` lists every user. ## 5. Cut the history into data points A data point is one piece of raw text with its time. Each data point has these fields: - `id`: the source id. Defaults to the lowercase SHA-256 of `content`. Must not contain `/`, and is at most 512 characters. - `content`: required. Non-blank, no U+0000, at most 500,000 characters. - `label`: at most 200 characters. - `timestamp`: ISO 8601 with a zone. Defaults to the stored value, else the current time. - `metadata`: a JSON object, at most 64 KiB serialized. - `identity`: at most 200 characters. - `idempotencyKey`: at most 200 characters. An unknown field returns `400 invalid-ingestion`. For a single data point, use `POST /v1/memory/ingest`. A response of `202` with `status: "queued"` means the send is accepted. Cut one thing that happened into one data point. [Sending data](/docs/memory-api/sending-data) gives the sizes and the id rules. Move to step 6 when every data point has an `id`, a `content` and a `timestamp`. ## 6. Send the history Send data points in batches with `POST /v1/memory/ingest/batch`. Each batch takes `items` (1 to 1,000 data points) and one `idempotencyKey`. Each batch is at most 16 MiB of UTF-8 in total. Each `id` may appear only once per batch. The whole batch is validated before any write. After that, each item is written on its own. If an item is refused, the earlier items stay accepted. The error returns `data: {ingestionId, accepted: k}`. Retry the same batch with the same `idempotencyKey` to resume at item `k`. The same `idempotencyKey` with the same body returns the original receipt. The same key with a different body returns `409 idempotency-conflict`. Response: `202` with `ingestionId`, `status: "queued"`, and one entry per item in `items`. A `200` with `status: "completed"` and `unchanged: true` means every item was already stored as sent. Keep each batch's `ingestionId` for step 7. ## 7. Wait for each batch Poll `GET /v1/memory/ingest/{ingestionId}` until the batch is done. The response has `status`, `settled`, `blocked`, `readiness`, and `failure`. - `status` is `processing` while any item waits in the retry queue. It is `failed` if any item is set aside after repeated failures. Otherwise it is `completed`. - `settled` is `true` when the first three `readiness` counts (`raw`, `comprehension`, `consolidation`) are 0 and `failed` is 0. The batch is finished. - `blocked` is `true` when the first three `readiness` counts are 0 and `failed` is above 0. The batch needs attention. Read `failure` for `{code, message}`. Move to step 8 when `settled` is `true` for every batch. If `blocked` is `true` for one, call `POST /v1/memory/ingest/{ingestionId}/retry`, or read `GET /v1/memory/ingest/{ingestionId}/explain` first. ## 8. Verify with a recall Call `POST /v1/memory/recall` with `query` and `identity`. Optional fields are `limit` (1 to 200, default 100), `level` or `maxTokens` (never both), `queryTimestamp`, `occurredFrom`, `occurredTo`, and `sort`. The response contains `asOf`, `usedEvidenceTokens`, `results`, and `pending`. Each result has `id`, `rank`, `occurredAt`, `content`, `artifact`, and `sources`. Check that your test data points appear in `results`. If `pending` is present, the user still has queued items. Wait for step 7 to finish, then recall again. Move to step 9 when a recall for a fact you sent returns it in `results[0]`, with the `sourceId` of the data point you sent. ## 9. Go live Check three things, then send production traffic. 1. Recall as a user who must not see a fact. The fact does not appear. 2. `GET /v1/memory/usage` returns the app's count for the month. 3. Your application stores the `X-Request-Id` of every call, so the [debugging](/docs/memory-api/debugging) reads can find a call later.