> ## Documentation Index
> Fetch the complete documentation index at: https://docs.praxa.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Test hosted memory candidates

> Create, lexically query, export, and erase a portable Praxa memory candidate while preserving the personal-tenant and source-provider boundaries.

The hosted memory candidate plane is deployed at `api.praxa.io`, but it remains
a qualification preview. Its unauthenticated gateway boundary is proven. A
complete authenticated positive, wrong-scope, revoked-key, and two-tenant by
two-subject canary matrix is still pending.

<Warning>
  Candidate records do not become Praxa personal memory, Vectorize content,
  Cognitive Events, AHCE observations, task context, or world-model facts. Use
  disposable test data until your authenticated qualification passes.
</Warning>

## Prerequisites

* A personal workspace key with `memory:write` for create and delete.
* A personal workspace key with `memory:read` for query and export.
* A subject identifier owned by your application.
* A provider/source record you can safely represent as a portable candidate.

Organization workspaces cannot mint hosted memory scopes. Identity and tenant
come from the authenticated key; neither is accepted from the request body.

## 1. Create one candidate

Save this body as `candidate.json`:

```json candidate.json theme={null}
{
  "apiVersion": "v1",
  "providerId": "custom",
  "sourceId": "customer-profile",
  "externalRecordId": "preference-42",
  "revision": "revision-1",
  "kind": "fact",
  "subject": "customer-42",
  "visibility": "subject",
  "content": "Prefers concise weekly summaries.",
  "metadata": {
    "category": "communication-preference"
  },
  "provenance": {
    "origin": "explicit",
    "confidence": 1,
    "capturedAt": "2026-08-13T12:00:00.000Z",
    "evidenceIds": [],
    "metadata": {}
  },
  "occurredAt": "2026-08-13T12:00:00.000Z",
  "observedAt": "2026-08-13T12:00:00.000Z",
  "originChain": []
}
```

```bash theme={null}
curl --fail-with-body https://api.praxa.io/v1/memory/records \
  -H "Authorization: Bearer $PRAXA_MEMORY_WRITE_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: tutorial-memory-create-0001" \
  --data-binary @candidate.json
```

The first successful request returns `201` with `replayed: false`. Persist
`candidate.id`. Replaying the exact body under the same key returns the same
candidate with `replayed: true`. A different body under that key returns `409`.

## 2. Query the subject

```bash theme={null}
curl --fail-with-body https://api.praxa.io/v1/memory/query \
  -H "Authorization: Bearer $PRAXA_MEMORY_READ_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "apiVersion": "v1",
    "subject": "customer-42",
    "query": "weekly summaries",
    "providerIds": ["custom"],
    "kinds": ["fact"],
    "limit": 20
  }'
```

The response always identifies `retrievalMode: "lexical"`. Do not describe
this hosted endpoint as semantic or graph retrieval.

## 3. Export candidate and deletion history

```bash theme={null}
curl --fail-with-body --get https://api.praxa.io/v1/memory/export \
  -H "Authorization: Bearer $PRAXA_MEMORY_READ_KEY" \
  -H "Accept: application/x-ndjson" \
  --data-urlencode "subject=customer-42" \
  --data-urlencode "limit=50"
```

The response is newline-delimited JSON. If `X-Praxa-Next-Cursor` is present,
send it as `cursor` on the next request. Each line is either a candidate event
or a content-free deletion receipt event.

## 4. Erase the candidate

```bash theme={null}
curl --fail-with-body \
  -X DELETE "https://api.praxa.io/v1/memory/records/$CANDIDATE_ID" \
  -H "Authorization: Bearer $PRAXA_MEMORY_WRITE_KEY" \
  -H "Idempotency-Key: tutorial-memory-delete-0001"
```

Deletion hard-erases the candidate content and returns an immutable receipt.
The receipt retains identifiers, digests, and timestamps, not the content. It
does not delete the original record from `customer-profile` or any other
provider-owned source.

## 5. Run the qualification matrix

| Check                             | Expected result                         |
| --------------------------------- | --------------------------------------- |
| Exact create retry                | Same candidate, `replayed: true`        |
| Changed body under create key     | `409 conflict`                          |
| Subject A query                   | Candidate appears                       |
| Subject B query in tenant A       | Candidate does not appear               |
| Subject A query in tenant B       | Candidate does not appear               |
| Read with write-only key          | Fails closed                            |
| Create with read-only key         | Fails closed                            |
| Request after key revocation      | Fails closed                            |
| Delete retry                      | Same deletion receipt, `replayed: true` |
| Query after delete                | Candidate content absent                |
| Export after delete               | Content-free deletion event present     |
| Source-provider read after delete | Original provider record unchanged      |

Run the matrix with two disposable tenants and two subjects per tenant before
using real customer data. Passing package tests or an unauthenticated `401`
probe does not substitute for this authenticated boundary test.

<Card title="Open the memory API playground" icon="flask" href="/api-playground/overview">
  Inspect the generated request schema and examples. Use only disposable
  least-privilege keys while the plane remains a qualification preview.
</Card>

## Troubleshooting

| Symptom                              | Resolution                                                                                                      |
| ------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
| Expected record is absent            | Verify provider-native data shape, namespace mapping, filters, and source status.                               |
| Another subject sees a record        | Stop the rollout and repair server-owned tenant-subject resolution before further tests.                        |
| Provider outage looks like no memory | Expose the source status and keep unavailable distinct from an empty successful recall.                         |
| Delete did not remove provider data  | Use the provider's lifecycle; read-only federation and hosted-candidate deletion do not erase provider sources. |

## Best practices

* Keep provider writes and lifecycle under the provider's documented API.
* Resolve tenant and subject in trusted backend code and test 2x2 isolation.
* Preserve provenance, source matches, contradictions, and per-source status.
* Bound concurrency, result count, context bytes, and source timeout.
* Never promote checkpoint, hidden, or unverified content into portable recall.

## Optimize for production

* Query only providers and record kinds relevant to the current purpose.
* Use bounded parallel recall and give each source an explicit timeout budget.
* Deduplicate normalized content while retaining every source match and contradiction.
* Measure source p50/p95 latency, partial/failed recalls, result precision, context bytes, and isolation failures.

Optimize only after the correctness and isolation matrix passes. Lower latency or cost is not an improvement if verified outcomes, authority checks, or recovery rates regress.

## Cleanup and next steps

1. Delete synthetic provider records using the provider's normal API.
2. Hard-erase disposable hosted candidates and verify content-free receipts when used.
3. Revoke test credentials and remove namespace fixtures for every tenant and subject.
4. Retain only non-content source statuses, identifiers, and test results required for audit.

After cleanup, run the [shared integration test matrix](/tutorials/test-your-integration) and record any environment-specific check that remains pending.

## Frequently asked questions

### What proves this tutorial works?

The minimum observable result is that create, lexical query, export, hard erasure, replay, and 2x2 isolation all pass. A compile, package import, mocked response, or initial admission alone does not prove the complete workflow.

### Can a browser, mobile app, or model prompt hold the credential?

No. Provider clients and Praxa credentials stay in the backend; adapters receive clients, not raw credentials.

### How should an ambiguous mutation be retried?

Persist the exact logical input and idempotency key before the first attempt. Reconcile through authoritative readback or replay the exact request with that same key before creating new work.

### What should we monitor after release?

Monitor per-source latency/status, partial and failed recall rates, result precision, context bytes, provenance coverage, and isolation failures. Alert on authorization bypass, cross-tenant disclosure, repeated conflicts, or cleanup failure.
