Skip to main content
This walkthrough puts the whole integration in one place: one agent per customer, one thread per conversation, and the header that says which of your customers a request acts for. The other walkthroughs each cover one call in more depth. You need an API key. See Authentication. The table below is the plan.

The model

Two things identify a request.
  • The key says which company is calling. It resolves to your organization, and your organization owns the agents, holds the credit, and is billed.
  • X-SP-End-User says which of your customers you are calling for. It is your own identifier for that person.
The boundary between your customers is the agent, not the header. Give each customer their own agent. An agent has its own memory and its own storage, so nothing crosses between two of them. The header works inside one agent, to keep one person’s conversations separate from another’s.

Step 1: mint a key

An administrator of your organization does this once, in the Priors application, under Settings → API keys. This is not an API call, and there is no endpoint for it. A key reaches your whole organization, so issuing one is an act of administration, and a key that could mint another key would turn one leak into an unbounded supply. The page shows the new key once:
Put the whole string straight into your secret manager, and send it unchanged as the bearer token. It is not a fragment to join with the key id. The page also shows an expiry date beside each key. Record it now: a key stops verifying on that date, nothing reaches your backend to say so, and an expired key returns the same 401 as an unknown one. So the first sign of a missed rotation is your own traffic failing. You can hold more than one key at a time, which is what makes rotation uneventful: mint the new key, deploy it, then revoke the old one from the same page. Revocation takes effect on the very next request, with no cache to wait out. Your organization can hold 5 live keys. If the page refuses a new one, revoke a key you no longer use.

Step 2: create one agent for one customer

Do this once per customer.
The agent is owned by your organization rather than by any person, and it appears in no directory of ours. external is the only kind your key creates, so you can leave visibility out entirely and receive it. Creation is asynchronous. Poll GET /v1/agents/{agent_id} until status is ready before the first turn. Store the returned agent_id against your own customer record. That mapping is the one piece of state you keep. Priors never needs to know what a customer of yours is. If it fails
  • 400 invalid_request: your request carried a Sec-Fetch-Site header, which means the key reached a browser.
  • 409 agent_quota_exceeded: your organization has reached its ceiling of 100 agents. One agent per customer means one hundred customers, and the 101st create is refused rather than queued. If you are heading past roughly eighty customers, contact us then rather than at the wall. Raising it is work on our side and it needs lead time.

Step 3: open a conversation for one person

The thread is recorded against that person. Send the same X-SP-End-User value on every later call for them. Threads carry a free-form metadata object, so you can tag one and match on it from the listing later. If it fails
  • 400 invalid_request: the header was sent empty or as whitespace, or it is longer than 255 characters. An empty value is never read as “no header”, because “no header” means your whole organization.
  • 404 not_found: the agent is not one this key reaches. Check that it is an external agent of your own organization.

Step 4: ask a question

response_context in the reply is the finished answer. Do not feed it to a model of your own. That asks a model to answer an answer. If you want material to answer from instead, call observe-and-enrich, whose request body is the same without learn_reply. detail: brief asks for a short answer that fits a phone screen; see Answer length. Learned memory is shared across the agent whether or not you send the header. Only the thread list narrows. Send the header on every call for that person, not only when you create the thread. Without it the key acts as the organization, and the per-subject rate window becomes one window shared by your whole backend. Say who is speaking. Set speaker on each observation to the person’s name ("speaker": "Amma"), not an id. The agent reads it when it reasons about the conversation and answers “who said…” questions from it. In a thread that several people share, leaving it out makes every message look like it came from the same person. Use tags for labels such as the channel ("tags": ["app"]); with speaker set, the agent keeps them with the message. Without speaker, the first tag is read as the speaker instead, which is the older behavior. speaker and X-SP-End-User do different jobs. speaker goes into the memory: it says who said a message. X-SP-End-User never reaches the memory: it decides rate limits and which threads a person can open. Send both. If it fails
  • 402 payment_required: your organization is out of credit. See Credits.
  • 429 rate_limit_exceeded: wait the number of seconds in Retry-After. With X-SP-End-User, the per-user window is counted against that person. Without it, every call your backend makes spends from one shared window.

Step 5: list one person’s conversations

There is no endpoint for this, and you do not need one.
GET /v1/agents/{agent_id} returns the agent with a threads array, and that array is already filtered for whoever is asking. With the header, it is that person’s conversations and nobody else’s, each with thread_id, title, metadata, and timestamps. Pick one and post to it, or create a new one. Without the header you are the organization, so you receive every thread on the agent. This also means you never lose a thread to a crash. If your backend dies after Priors creates a thread but before you commit the id, the thread is still recorded against that person and appears in the very next listing.

Keep the key on your server

The key belongs on your backend. It must never reach a browser or a mobile app. A key in front-end code is in your bundle, and anyone who loads the page can read it. Two things stop that happening by accident. Priors sends no CORS headers, so a browser blocks the call before it leaves the page. And a request that carries a key and a Sec-Fetch-Site header is refused with 400 invalid_request, because only a browser sets that header. Priors does not check Origin or Referer. If your own gateway or HTTP library adds either header, nothing here minds.

Errors for a key

Every error body carries code, error, and request_id. Branch on code, not on the message. Quote request_id when you report a problem.

Generate a client

https://www.sapientpriors.com/docs/openapi.yaml is a complete OpenAPI 3.0 document. Any generator produces a typed client in your language, with both headers already wired up.