Skip to content

Quickstart

From an empty account to a memory you can search

Seven steps, about five minutes, and one of them is irreversible. Step two shows a secret exactly once — read it before you run it.

  1. Create an account

    Sign up with an email, a password and a name. The address has to be confirmed before the account can be used, so this call returns no session — it sends a link instead. Open the link, then sign in; that second call is what sets the cookie the next step needs.

    POST /api/auth/sign-up/email
    curl -fsS -X POST https://pfmem-api.packagefactory.dk/api/auth/sign-up/email \
      -H 'content-type: application/json' \
      -c cookies.txt \
      -d '{"email":"you@example.com","password":"a-long-passphrase","name":"Your Name"}'

    Until the address is confirmed, sign-in answers 403 with EMAIL_NOT_VERIFIED. The link is valid for one hour, and the page it lands on can send another.

    POST /api/auth/sign-in/email
    curl -fsS -X POST https://pfmem-api.packagefactory.dk/api/auth/sign-in/email \
      -H 'content-type: application/json' \
      -c cookies.txt \
      -d '{"email":"you@example.com","password":"a-long-passphrase"}'

    You can do this in the browser instead. The result is the same session cookie. Signing up with an address that already exists gets the same answer as a new one — the response is deliberately not a way to ask which addresses are registered.

  2. Mint the master key

    This one call creates the account record and issues its first master key. It authenticates with the session cookie rather than a bearer key, and — because a cookie rides along on its own — it must echo this session's CSRF token in a header. Read the token first, then mint.

    GET /api/v2/account/csrf-token
    curl -fsS https://pfmem-api.packagefactory.dk/api/v2/account/csrf-token \
      -b cookies.txt
    POST /api/v2/account/master-key
    curl -fsS -X POST https://pfmem-api.packagefactory.dk/api/v2/account/master-key \
      -H 'content-type: application/json' \
      -H 'x-csrf-token: <the token from the call above>' \
      -b cookies.txt \
      -d '{"label":"bootstrap master key","account_name":"Acme"}'
    200 OK
    {
      "request_id": "23ba77f0c6474ccb8dc5d65d1a44d069",
      "data": {
        "key": {
          "key_id": "key_01HQ",
          "tier": "master",
          "label": "bootstrap master key",
          "prefix": "mk_live_",
          "tenant_slug": null,
          "role": null,
          "verbs": ["read", "write", "delete"],
          "created_at": 1755640000000
        },
        "secret": "mk_live_<shown here and nowhere else, ever>"
      }
    }

    Calling this a second time is refused, because an account holds one first master key and there is nothing to re-issue. If the secret is gone, the way back is rotation, not a retry.

  3. Create a workspace

    A workspace is a boundary around one set of memories — one per customer, per environment, or per project. Creating one gives it a private space of its own, with nothing else in it and nothing shared with any other workspace. The slug is yours to choose within the allowed shape; the internal name it resolves to is derived from it and returned to you, never sent.

    POST /api/v2/admin/tenants
    curl -fsS -X POST https://pfmem-api.packagefactory.dk/api/v2/admin/tenants \
      -H 'authorization: Bearer mk_live_...' \
      -H 'content-type: application/json' \
      -d '{"slug":"acme_prod","display_name":"Acme (production)",
          "active_embedding_version":"external_v1"}'

    Authenticate with the master key as a bearer token from here on. A slug is lowercase letters, digits and underscores, up to forty characters — a hyphen is a validation error, not a slug with a hyphen in it. The embedding version names an entry that already exists in the registry; it is not a label you invent per workspace.

  4. Mint a key scoped to that workspace

    Master keys are for administration. Day-to-day traffic should carry the narrowest credential that still works, so mint a workspace-scoped key and give that to the client. The tier and the slug in the body below are what narrow it.

    POST /api/v2/admin/keys
    curl -fsS -X POST https://pfmem-api.packagefactory.dk/api/v2/admin/keys \
      -H 'authorization: Bearer mk_live_...' \
      -H 'content-type: application/json' \
      -d '{"tier":"tenant","label":"acme_prod client","tenant_slug":"acme_prod"}'

    The request body is validated strictly: an unrecognised field is a validation error rather than a silently ignored one, so a misspelt name fails loudly instead of widening nothing.

  5. Install the client and store the key

    Pipe the key in on standard input rather than passing it as an argument. An argument shows up in the process list for every user on the machine and is written to your shell history; standard input is neither.

    pfmem init
    curl -fsSL https://pfmem-api.packagefactory.dk/install.sh | sh
    export PATH="$HOME/.pfmem/bin:$PATH"
    cat key.txt | pfmem init

    The stored config file is created with owner-only permissions, and the key it holds is only ever sent to an https origin or to loopback.

  6. Store a conversation

    Ingestion is asynchronous. The call returns as soon as the messages are durable and hands back a task id; extraction into searchable memories happens behind it. Nothing is searchable the instant this returns, which is what the next step is for.

    pfmem add
    pfmem add --mode chat --user-id u_42 --session-id s_1 \
      --idempotency-key batch-2026-08-18-01 \
      --messages '[{"role":"user","content":"I moved to Berlin"}]'

    The idempotency key makes a retry after a timeout safe: a replay returns the first result instead of ingesting the same batch twice.

  7. Read it back

    Force the pending extraction rather than waiting for the background pipeline, then ask a question. The flush blocks for as long as you allow it; the search matches your question against what the workspace holds — by meaning as well as by exact wording — and returns a ranked set of facts, not a transcript.

    pfmem flush · pfmem search
    pfmem flush --session-id s_1 --wait-ms 10000
    pfmem search --memory-type user --user-id u_42 --query "where does he live now?"

    Use the ranked search when you have a question. Use the plain listing when you want everything held about a subject, in a stable order, with real pagination.

    pfmem get
    pfmem get --memory-type user --user-id u_42 --limit 50

Where to go from here

The command reference covers every flag, the exit codes and the log file. The API reference covers the envelope, the status codes and the endpoints these commands call.