Subcore AI Docs
Agents API

Instructions

The prompt files an agent answers with. How to list, write and remove them, the path grammar, and the two guards that keep a database agent answerable.

An agent on instructionSource: "database" answers with the files stored here, laid over the deployment's own by path. These routes manage them. Prompt files covers what goes inside one.

RouteWhat it doesSuccess
GET /agents/{slug}/instructionsThe stored files by directory, content included200 { "instructions": { "main": [File] } }
PUT /agents/{slug}/instructions/{path}Create or replace one file from { "content": "…" }201 created, 200 replaced: { "file": … }
DELETE /agents/{slug}/instructions/{path}Remove one file204, no body

All three need the manage-agents permission.

The File object

{
  "path": "instructions.md",
  "content": "…",
  "versionId": "sha256-860e7daee313",
  "updatedAt": "2026-09-16T18:00:21.418Z"
}
FieldTypeDescription
pathstringRelative to the directory's instructions/, in the grammar below
contentstringThe file's text, verbatim
versionIdstringsha256- and the first 12 hex characters of the SHA-256 of the content
updatedAtstringWhen the file was last written. ISO 8601 UTC with milliseconds and a Z, such as 2026-09-16T18:00:21.418Z

versionId is computed from the content, and it is byte-identical to the id your deployment computes for the same file on disk. Two files with the same text have the same id wherever they live. That is what lets a stored file and its on-disk twin land in one provenance record instead of reading as a change that never happened.

Directories

Files live under a directory, given as ?directory= on the write and delete routes and defaulting to main. Lowercase letters, digits and hyphens, starting with a letter or digit, at most 64 characters.

The name picks which of your deployment's own agent definitions the files belong to, matching a folder under agents/ in its tree. Deployments built from the scaffold define one, main, which is why main is the default and why most partners never send the parameter. Send another name only when the deployment you are storing files for defines another; files under a name it does not define fail that agent's calls at the deployment rather than being ignored.

A directory belongs to one agent. Stored files are keyed by agent, directory and path together, so main under one agent and main under another are separate sets that never collide, however many agents your deployment answers for. There is nothing to name uniquely and nothing to keep apart by hand.

An empty ?directory= is refused with 400 INVALID_DIRECTORY on the write and delete routes. It does not fall back to main; leave the parameter out for that. The list route does not read the parameter at all.

Paths

The file path is the rest of the URL, slashes included:

PUT /agents/clinic-one/instructions/kit/identity.md

A path is either entries.json or a lowercase name made of letters, digits, ., _, / and - that starts with a letter or digit and ends in .md. It may not contain .. anywhere, and it is at most 200 characters. Anything else is 400 INVALID_PATH. Every markdown path you can store here is one another file can pull in, writing it with or without the .md.

entries.json is the one file that is not markdown. It is an object that maps each entry key to a prompt path. Every value has to be a prompt path in the same grammar with no .. segment, or the write is refused with 400 INVALID_ENTRIES.

What entries.json is for

Your deployment compiles a prompt for each of its runtime entry keys, and this map is what says which file each key is built from. A deployment that answers voice, SMS and chat has a key for each, and its own map might read:

{
  "frontdesk.voice": "voice.md",
  "frontdesk.sms": "sms.md",
  "frontdesk.chat": "chat.md"
}

The keys are your deployment's, not Subcore's, so read them out of its own tree. Storing this file is optional, and most agents never need it: leave it out and the agent uses the deployment's map, with your stored markdown files resolved through it.

A stored entries.json replaces the deployment's map rather than merging into it. The one you store becomes the whole map, so it has to carry every key the deployment uses; a key you leave out is gone rather than inherited, and the first call that needs it fails. Copy the deployment's map and edit it, rather than sending only the keys you are changing.

A value may name a file that exists only here, since the map is read after your stored files are laid over the deployment's own. A value that names a file neither side has costs nothing at write time and fails every call that agent takes, so check the map against both sets of files before you store it.

List files

GET /agents/{slug}/instructions

Every file stored for the agent, grouped by directory, content included. No other route reads a stored file back.

curl -H "Authorization: Bearer $SUBCORE_API_KEY" \
  "https://api.subcore.ai/agents/clinic-one/instructions"
{
  "instructions": {
    "main": [
      { "path": "instructions.md", "content": "…", "versionId": "sha256-860e7daee313", "updatedAt": "…" },
      { "path": "kit/identity.md", "content": "…", "versionId": "sha256-1c1e2f0a9b3d", "updatedAt": "…" }
    ]
  }
}

The listing shows what is stored, not what a call is served. An agent on repo has its stored files listed too, even though no call uses them. The config read, by contrast, answers "instructions": {} for a repo agent.

This route takes no parameters. ?directory= is read by the write and delete routes and not by this one, so it changes nothing here: with it, without it, or with a name no file uses, the answer is the same and holds every directory the agent has. Pick the directory you want out of the object rather than asking for it.

Write a file

PUT /agents/{slug}/instructions/{path}

Create or replace one file. The body must be JSON with a single content field; any other field is 400 UNKNOWN_FIELD.

curl -X PUT -H "Authorization: Bearer $SUBCORE_API_KEY" \
  -H "Content-Type: application/json" \
  "https://api.subcore.ai/agents/clinic-one/instructions/kit/identity.md" \
  -d '{ "content": "You are the front desk of Clinic One. …" }'
{
  "file": {
    "directory": "main",
    "path": "kit/identity.md",
    "versionId": "sha256-1c1e2f0a9b3d",
    "updatedAt": "2026-09-16T18:00:21.418Z"
  }
}

201 means the file was created and 200 that it was replaced. The answer says where the file was stored and what your deployment will call its bytes. It does not echo the content back, so it is not the File object above. It has four fields:

FieldTypeDescription
directorystringThe directory the file was stored under: main unless ?directory= said otherwise
pathstringThe path written, in the grammar above
versionIdstringThe same id the File object carries
updatedAtstringWhen the file was written, in the same ISO 8601 UTC form as above
RuleRefusal
content must be a string400 INVALID_CONTENT
No NUL character and no unpaired surrogate400 INVALID_CONTENT
At most 256 KiB of UTF-8413 CONTENT_TOO_LARGE
The request body as a whole, at most about 1.5 MB413 PAYLOAD_TOO_LARGE

Content is stored verbatim and never normalised, because the deployment hashes the exact bytes it compiles.

Remove a file

DELETE /agents/{slug}/instructions/{path}
curl -X DELETE -H "Authorization: Bearer $SUBCORE_API_KEY" \
  "https://api.subcore.ai/agents/clinic-one/instructions/kit/identity.md"

204 with no body. A path with no file behind it is 404 FILE_NOT_FOUND. That is a different code from 404 NOT_FOUND, which means the agent itself was not found.

How stored files meet the deployment's own

Your deployment holds a set of prompt files of its own. On a call for an agent on database, it lays the agent's stored files over that set, matching them up by path:

A pathWhat the call compiles from
Stored here and in the deploymentThe stored file. It replaces the deployment's for this agent, and leaves it alone for every other one
Only in the deploymentThe deployment's file, inherited
Only stored hereThe stored file, added to the set

The third row is worth stating plainly, because "override" suggests otherwise. A path your deployment has never had is not ignored: it joins the file set for that agent's calls, and a [[partial]] reference in another file resolves against it like any other. So one agent can be given a section the deployment does not ship, without a deployment change, by storing the file and referencing it from a file that is stored too.

Files are the unit. Storing one file does not hide the rest, and there is no way to remove one of the deployment's files for one agent; storing an empty file at that path is the nearest thing.

The two guards

An agent on database with no file stored fails every call it takes, so the API refuses the two writes that would produce that state:

WriteRefusal
Switching an agent to database, or creating one on it, while it has no file stored409 NO_INSTRUCTIONS
Deleting the only file of an agent on database409 LAST_INSTRUCTION_FILE

So the order is files first, then the switch. To remove the last file, switch the agent to repo first.

Each guard reads and then writes, so two requests arriving at the same moment can slip past it. Send the writes for one agent one after another, not concurrently.

Nothing here compiles what it stores. A file is checked against the path grammar above and its size, not built into a prompt, so the rules on prompt files are all checked at your deployment instead. A [[partial]] neither you nor your deployment supplies is stored without complaint and fails that agent's calls there. Run a turn through the agent before pointing a phone number at it.

On this page