Open the console

API

Artifacts

Files an agent can read. Uploading is a two-call handshake because the bytes go straight to storage and never through this platform; reading one back is a short-lived URL for the same reason.

A scope is a filename prefix

project:policy.pdf, agent:rates.csv, user:scan.pdf, or a bare scan.pdf for this conversation only. The prefixes are the agent framework's own idiom, so the same name works in your tool code via load_artifact(filename). A name that comes back from the listing KEEPS its prefix — hand back a bare policy.pdf and it resolves to a different file, or to nothing.

The two shared scopes are admin-only to write

project: and agent: are a tenant's reference material, readable by an agent and written by a tenant admin. A non-admin write is a 403 naming the scope.

A ticket cannot reach any of this

An end user's ticket opens the event stream and nothing else; every route here refuses one with 403. A browser uploading for one of your users gets a URL your backend declared with its own credential, naming them in user, and PUTs the bytes there.

Always send project

Empty means the platform's own project, not yours: a project-scoped credential is refused with 403, and any other addresses a bucket that holds none of your files. Both SDKs fill it in from the client's project.

user is one of your end users

Your own id for them — ada or eu:ada, read the same way. Any value is taken as an end user, so a colleague's files cannot be named here; omitted, the files are the caller's own. Any member of the tenant may name any of your end users.

The samples use $API and $TOKEN, which the API overview sets.

Declare an upload

POST/v1/artifacts/uploadsrt-service

Authorizes one upload and hands back a URL to PUT the bytes to, with the exact headers to send.

Send the headers back exactly as given, and send nothing else

The signature covers them. One header out of place, or one added, and storage refuses the PUT — which is the main reason to let the SDK do this when your own process holds the bytes.

Never send your platform token to the returned URL

It is not on this platform. The signature IS the authorization, so an Authorization header there would put a credential somewhere it has no business being.

The upload IS the artifact

The object lands exactly where the agent framework would have written it, so there is nothing to register afterwards: your tool finds it with load_artifact(filename) and the agent can discover it by listing. Saving the same filename again creates a new version rather than overwriting.

Request
# 1. Declare it.
UPLOAD=$(curl -s -X POST "$API/v1/artifacts/uploads" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"target_slug": "hotel-concierge", "project": "agents",
       "session_id": "sess-ada-1", "filename": "scan.pdf",
       "content_type": "application/pdf", "size": 18342}')

# 2. PUT the bytes, with exactly the headers it gave you.
curl -X PUT "$(echo "$UPLOAD" | jq -r .url)" \
  -H "Content-Type: application/pdf" \
  -H "x-goog-content-length-range: 18342,18342" \
  --data-binary @scan.pdf

Body

FieldTypeDescription
target_slugstringrequiredThe target whose store this belongs to.
session_idstringrequiredThe conversation. It is part of the address for the session scope.
filenamestringrequiredWith a scope prefix, or bare for this conversation only.
content_typestringrequiredThe real one. It is what the model is told the file is.
sizeintegerrequiredExactly how many bytes will be PUT. Up to 512 MiB. This is pinned into the signature, so it must be exact rather than an upper bound.
projectstringoptionalThe project the target is in. Send it: empty is the platform's own.
userstringoptionalOne of your end users, whose file this is. Omitted, the caller's own.

Responses

StatusMeaning
201Authorized. Carries the URL, the method, the headers and an expiry.
401No credential, or one that does not verify.
403A ticket; a non-admin writing the project or agent scope; user=*; or a project the credential is not scoped to, an empty one included.
422The filename or the size was refused — over 512 MiB, or zero — or an unknown field, naming the field you supplied rather than a storage path.
501This deployment has no artifact transfer configured.

Response body

{
  "url": "https://storage.googleapis.com/...&X-Goog-Signature=...",
  "method": "PUT",
  "headers": {
    "Content-Type": "application/pdf",
    "x-goog-content-length-range": "18342,18342"
  },
  "expires_at": "2026-10-17T09:29:02Z",
  "max_bytes": 18342,
  "filename": "scan.pdf",
  "version": 0
}

List what a conversation can see

GET/v1/artifactsrt-service

Every filename reachable from one session, across all four scopes, with the prefixes intact.

Omit session_id for the shared scopes

The project and agent scopes have no session, so a listing of them cannot require one. Pass it to see a conversation's own files as well — which is what you want before an invocation, and not what you want when rendering a tenant's document library.

Request
curl "$API/v1/artifacts?target_slug=hotel-concierge\
&project=agents&session_id=sess-ada-1" \
  -H "Authorization: Bearer $TOKEN"

Parameters

FieldTypeDescription
target_slugqueryrequiredThe target whose store to read.
session_idqueryoptionalInclude this conversation's own files. Omit for the shared scopes only.
projectqueryoptionalThe project the target is in. Send it: empty is the platform's own.
userqueryoptionalOne of your end users, whose files to include. Omitted, the caller's own.

Responses

StatusMeaning
200{artifacts} — a list of names.
401No credential, or one that does not verify.
403A ticket, user=*, or a project the credential is not scoped to, an empty one included.
422No target_slug.
501This deployment has no artifact transfer configured.

Response body

{
  "artifacts": [
    "project:refund-policy.pdf",
    "agent:rates-2026.csv",
    "user:passport.jpg",
    "scan.pdf"
  ]
}

Get a download URL

GET/v1/artifacts/downloadrt-service

A short-lived URL for one file. A browser can navigate straight to it.

A URL rather than the bytes, and it needs no CORS

Nothing reads the response from script, so navigating or setting it as an href works with no cross-origin configuration at all.

Omitting version means the newest

Which is what saving the same filename twice produces. Pin one only if you kept the number — a retention rule deletes old versions before the newest, so a pinned old version can legitimately disappear while the file still exists.

Request
curl "$API/v1/artifacts/download?target_slug=hotel-concierge\
&project=agents&session_id=sess-ada-1&filename=scan.pdf" \
  -H "Authorization: Bearer $TOKEN"

Parameters

FieldTypeDescription
target_slugqueryrequiredThe target whose store to read.
session_idqueryrequiredThe conversation.
filenamequeryrequiredWith its scope prefix, as the listing gave it.
versionqueryoptionalA specific version. Omitted, the newest.
attachmentqueryoptionaltrue by default — the browser saves it. false lets it render inline.
projectqueryoptionalThe project the target is in. Send it: empty is the platform's own.
userqueryoptionalOne of your end users, whose file it is. Omitted, the caller's own.

Responses

StatusMeaning
200{url, filename, scope}.
401No credential, or one that does not verify.
403A ticket, user=*, or a project the credential is not scoped to, an empty one included.
404No artifact by that name.
422The filename was refused — most often a prefix that is not a scope — or target_slug, session_id or filename is missing.
501This deployment has no artifact transfer configured.

Delete a file

DELETE/v1/artifactsrt-service

Removes one artifact and every version of it. There is no undo.

The one operation with no signed URL

This service can act as the signer directly, and a signed DELETE would put a destructive credential in a browser. The shared scopes are admin-only to delete from; a conversation's own files and a user's can be deleted by any member of the tenant.

Request
curl -X DELETE "$API/v1/artifacts?target_slug=hotel-concierge\
&project=agents&session_id=sess-ada-1&filename=scan.pdf" \
  -H "Authorization: Bearer $TOKEN"

Parameters

FieldTypeDescription
target_slugqueryrequiredThe target whose store to write.
filenamequeryrequiredWith its scope prefix.
session_idqueryoptionalRequired for a session-scoped file.
projectqueryoptionalThe project the target is in. Send it: empty is the platform's own.
userqueryoptionalOne of your end users, whose file it is. Omitted, the caller's own.

Responses

StatusMeaning
204Gone.
401No credential, or one that does not verify.
403A ticket; a non-admin deleting from the project or agent scope; user=*; or a project the credential is not scoped to, an empty one included.
422The filename was refused.
501This deployment has no artifact transfer configured.

Response body

204 — no body. There is deliberately nothing to read back.

Reading this as a model? llms.txt indexes every page, and each page has a “Copy for AI” control.