Skip to main content
This page walks through the whole API in the order a product uses it, from your organization to the deletion of an agent. Read it once from top to bottom. After that, use it as a reference: each section has the request, the success response, and a table of errors with what to do. Every example uses the base URL https://app.sapientpriors.com/api, and carries your API key. See Authentication. Two errors can happen on any call, so the tables below do not repeat them:
  • 401 unauthorized: your key is missing, unknown, revoked, or expired. Check the key.
  • 503 service_unavailable: a part of Priors did not respond, or the agent is restarting. Wait a few seconds, then send the same request again.
Add X-SP-End-User to any call inside a thread to act for one named person on your side. It decides which threads you reach and which rate-limit window the call spends from. See Call Priors from your backend.

Stage 0: your key

Your organization exists before your first API call, and an administrator of it mints your key in the Priors application under Settings → API keys. There is no endpoint for that. See Authentication. GET /v1/session confirms which organization your key resolves to, and returns the upload limits your organization is held to. Call it once when your client starts.

Stage 1: create an agent

Send POST /v1/agents. You become the owner. Only the owner can change the agent, delete it, or share it.
Success: status 202. The agent_id is final. The agent is not ready yet.
Then: call GET /v1/agents/{agent_id} every second until status is ready. This usually takes a few seconds. An agent that is still initializing after 10 minutes becomes failed. A failed agent does not recover: delete it and create a new one. Errors: You can also send agent_config.metadata (your own labels, returned unchanged) and ttl_seconds. Agents do not expire today. ttl_seconds is stored for a future feature, and expires_at is always null. An agent exists until you delete it. visibility is always external for an agent your key creates, so you can leave it out. Your organization owns the agent rather than any person, and it appears in no directory. Keep your own record of which agent_id belongs to which of your customers. Every later call puts it in the path. metadata is a label, not an index: Priors stores it and hands it back unchanged, but no call finds an agent by it. Priors cannot check that mapping for you, so if your lookup returns the wrong agent_id the call still succeeds, and one customer reads another customer’s memory.

Change an agent later

PATCH /v1/agents/{agent_id} changes agent_config. The memory is not touched. visibility cannot change. While the agent is initializing, the call returns 409 agent_not_ready. Two rules apply:
  • agent_config replaces the whole object. Send role, objective, and metadata together. A field you leave out is cleared.

An agent id never changes

To move a user to another agent, open a thread on the other agent. Nothing moves between agents. If two groups of people must not share a memory, give them two agents.

Stage 2: open a thread

Send POST /v1/agents/{agent_id}/threads. Use one thread for each conversation, task, or user session.
Success: status 201 with a thread_id. Errors:
  • 409 agent_not_ready: poll the agent until it is ready.
  • 404 agent_not_found: the id is wrong, or the agent is not yours.
  • 429 rate_limit_exceeded: more than 5 threads in 10 seconds.
GET .../threads/{thread_id} returns the thread with up to its 200 most recent messages. Each message has speaker, text, timestamp, and source_ids. PATCH renames the thread. DELETE removes it and returns 202. Deleting a thread does not remove what the agent learned from it.

Stage 3: teach the agent

Four calls send observations to an agent. They differ in what the response contains. These four calls are the center of the API.

The observation: the format the first three calls share

Images: the three ways

Priors learns images as text. At some point, an image becomes a description, and the agent remembers the description. There are three ways to send an image. They are not interchangeable. When the image is a file on a computer or a phone: the observe calls do not accept bytes. You have two options:
  1. Put the file where Priors can download it, for example your own storage with a signed URL that expires soon. Send that URL in img_url. Priors cannot download a URL that needs a login, a URL on a private network, or a URL on localhost.
  2. Describe the image yourself and send the words. Use your own image model, or use the caption your user wrote. Send the words in blip_caption or in content.
If the image belongs to a conversation, use way C. What the response does not tell you: with way A, Priors returns 202 before it downloads the image. If the download fails, or the image cannot be described, the observation is still learned from your text and blip_caption only. The status does not report the failure. Always send a blip_caption when you have one. Images in observe-file: the uploaded file can itself be an image. observe-file accepts one file per call. A PNG, JPEG, GIF, or WebP file is read as a one-page document: Priors extracts the text in the image, for example the text in a photographed receipt or a screenshot, and learns it. Pictures inside a PDF are extracted and described in place. To send several files, send several calls, one after another.

observe: learn only

Success: status 202 with {"observation_id": "obs_01J...", "accepted": true}. Nothing is learned yet. Then: call GET .../observe/{observation_id}/status every second until status is committed. The table below lists every status value. POST .../observe/{observation_id}/cancel stops learning that has not finished. Pieces that were already learned stay learned. Errors:
  • 400 or 422 invalid_request: empty content, a block that is not text, or more than 100 observations.
  • 402 payment_required: no credits.
  • 404 thread_not_found: the thread or the agent does not exist, or you cannot open it.
  • 409 agent_not_ready: poll the agent until it is ready.
  • 429 rate_limit_exceeded: more than 60 calls in one minute, or Priors is accepting another observe for this agent at this moment (Retry-After: 1).
Cost: the words you send.

observe-and-enrich: memory for your own model

The request body is the same as for observe. You can add detail (brief, standard, or detailed) to set how much the agent writes, and max_context_chars for a hard limit on size. See Answer length. Success: status 200.
response_context is memory text for your prompt. It is not an answer. used_memory_ids and used_source_ids tell you which memories and files were used. An empty response_context means the agent has no related memory yet. That is a success, not an error. Errors: 400 or 422, 402 payment_required (no credits), 404, 429 (more than 20 calls in one minute, or 2 answers already running), 503 answer_model_unavailable (wait, then try again). Cost: the words you send plus the words in response_context.

observe-and-respond: an answer from Priors

The request body is the same, plus learn_reply. Success: status 200 with the same fields. Here response_context is the answer. Show it to the user. Do not send it to another model. The agent learns the answer it writes, as chat does. Send "learn_reply": false if you also send the answer back as an observation, or if answers must not become memory. Errors and cost: the same as observe-and-enrich.

chat and chat/stream: one message in a chat

turn_id is an id you create. It must be unique for each message and at most 200 characters. If you send the same turn_id again, Priors charges it only once. message is 1 to 2,000 characters. Longer text belongs in observe. detail (brief, standard, or detailed) sets how long the answer is. Success: status 200 with message (the answer), memory_context, citations (a list with source_id, label, href, verified), duration_ms, stages (memory_context, final_generation, turn_learning, each with duration_ms), and learning (earlier messages of the thread that were not learned; usually empty). verified is true only for an image attached to this message. It is false for a document learned earlier. chat/stream returns server-sent events: stage ({"stage": "memory_context"}), delta ({"delta": "..."}, add it to the text on the screen), then one complete event (the full response) or one error event ({"error", "code", "request_id"}). Errors:
  • 400 or 422 invalid_request: message is longer than 2,000 characters, or turn_id is missing.
  • 402 payment_required: no credits.
  • 404 thread_not_found: the thread or the agent does not exist, or you cannot open it.
  • 404 not_found: an id in image_source_ids is not an image this agent holds.
  • 409 agent_not_ready: poll the agent until it is ready.
  • 413 payload_too_large: more than 10 images, or more than 32 MB of images.
  • 429 rate_limit_exceeded: more than 20 messages in one minute, or 2 answers already running for this subject.
  • 503 answer_model_unavailable: wait, then send the same request again.
After a stream has started, the same codes arrive in the error event. Cost: the words in the message you send plus the words in the message you receive.

There are no webhooks

Priors never calls your server. To know when learning is done, poll the status. Answers are returned in the response. Send your own request_id on answering calls to match them with your logs.

The order in which observations are learned

  • Observations in one thread are learned in the order Priors received them. Inside one call, the order of the array is kept.
  • Two calls that arrive at the same time for the same thread have no defined order. To keep the order across calls, wait for the 202 of one call before you send the next. To send old messages, send them in one call, in order.
  • timestamp sets the time of the memory. It does not change the order of learning.
  • A question sent before an observation is committed can be answered without that observation. Poll first when the answer depends on what you just sent.

Stage 4: teach from a file

Send POST /v1/agents/{agent_id}/threads/{thread_id}/observe-file as multipart/form-data. One file per call.
Success: status 202.
The file became 64 pieces. Poll the status in the same way as for observe. Save the source_id: answers point to the file with it. Limits: one file read at a time per agent, three at a time per organization, 2,000 pieces per file, and one upload every 10 seconds per subject. To load ten files into one agent, upload them one after another. You do not need to wait for committed: a file sent while the agent reads another is accepted with queued: true and starts by itself. If an upload stops: a Priors restart only pauses it (interrupted), and it resumes by itself. An upload that stopped partway or failed can be finished with POST .../sources/{source_id}/finish-learning, which reads and charges only what is left. See Sources. Errors: Priors converts and splits the file before it returns 202. So every problem with the file itself is an error on this call. Learning can still stop after the 202. The status then reads failed, and error says why. Correct what it names and upload the file again. The full list of causes is on Learning. A second upload of exactly the same bytes into the same thread returns 202 with "duplicate": true and costs nothing. The same bytes into another thread of this agent return 409 document_already_ingested, because what the first read learned is already searchable from every thread. Send allow_duplicate=true to read it again and store a second copy. To retry an upload safely: send an Idempotency-Key header, any string up to 255 characters, unique for this upload. If you send the same key with the same file again, Priors returns the original 202 and does not read or charge the file again. The same key with a different file or different split settings returns 409. Priors keeps a key for 24 hours. Cost: the words of every piece. A refused file or a duplicate costs nothing.

Stage 5: images and sources

Upload an image for a chat message

Send the bytes with PUT /v1/agents/{agent_id}/threads/{thread_id}/sources/images. Set Content-Type to image/png, image/jpeg, or image/webp. Up to 10 MB. Success: status 200 with source_id, label, media_type, href, and verified. Send the source_id in image_source_ids on a chat message. Up to 10 images per message. Errors:
  • 400 invalid_request: the Content-Length header is missing or is not a number.
  • 404 thread_not_found: the thread or the agent does not exist, or you cannot open it.
  • 413 payload_too_large: the image is larger than 10 MB.
  • 422 invalid_request: the bytes are not an image that can be decoded.
  • 429 rate_limit_exceeded: more than 20 uploads in one minute.
  • 503 service_unavailable: image storage is not available.

List and download the files an agent holds

Errors: the list returns 400 invalid_request for an old cursor (start again without one) and 503 when storage does not respond. A download returns 404 not_found when the agent has no source with that id, and 503 when storage does not respond.

What you cannot list

You cannot list memories. There is no call that returns them. Memories reach you only inside answers, as used_memory_ids and as the text that was written from them. To learn what an agent knows, ask it. To see what the agent was given, read the thread and the list of sources.

Stage 6: see who used the agent

GET /v1/agents/{agent_id}/logs returns which thread was used, by which subject, and when. It never returns the text of a message. Send X-SP-End-User on your calls and each turn is attributed to the person it was taken for.

Stage 7: credits

GET /v1/billing returns your organization’s balance and every charge. The calls that cost credits are the four calls in Stage 3 and observe-file. Every other call is free. When the balance is zero, the calls that cost credits return 402 payment_required, and the free calls keep working. See Credits.

Stage 8: delete

The whole lifecycle in one table