Skip to content

Agents (A2A)

llms.txtAsk agent

Original Pictures runs a small A2A (Agent2Agent) server, so another agent can sign media, read what a file’s Content Credentials declare, and look up AI-disclosure rules without writing REST calls. Each skill is a thin mapping onto a REST operation: the same service runs, with the same scopes, refusals, billing and per-key rate limits.

What Where
Agent card GET https://api.originalpictures.com/.well-known/agent-card.json
JSON-RPC endpoint POST https://api.originalpictures.com/a2a
Protocol versions A2A 1.0 (SendMessage, GetTask, CancelTask) and the 0.3 names (message/send, tasks/get, tasks/cancel) on the same endpoint
Streaming, push, extended card Not offered (the card says false for each)

The method name decides the answer’s dialect: a 1.0 method gets 1.0 shapes (ROLE_AGENT, TASK_STATE_COMPLETED, parts without kind), a 0.3 method gets 0.3 shapes (kind, agent, completed). An A2A-Version header, when sent, must be 1.0 or 0.3; anything else is -32009.

Send an API key as a bearer token, exactly as for the REST API (Authentication). A missing or invalid key is refused with HTTP 401 before any JSON-RPC is read. The endpoint admits a key that holds at least one scope a skill uses; each skill then needs the scope its REST operation needs, and answers HTTP 403 without it:

Skill / method Scopes REST equivalent
sign sign:write; plus uploads:write for an inline file POST /v1/uploads then POST /v1/sign
verify verify:write; plus uploads:write for an inline file POST /v1/uploads then POST /v1/verifications
disclosure-policy any key the endpoint admits GET /v1/disclosures/policies
GetTask on a sign job jobs:read (and assets:read for the download link) GET /v1/jobs/{job_id}
GetTask on a verification recovery:read GET /v1/verifications/{verification_id}
CancelTask on a sign job jobs:write POST /v1/jobs/{job_id}/cancel

A verification task carries the full result, so reading one needs recovery:read, as its REST equivalent does; verifications:read covers only the verification history and listing. A verification cannot be canceled: CancelTask on one reads it (recovery:read) and answers -32002.

A publishable disclosure key (scope disclosure:write alone) cannot use the endpoint. Every A2A call counts as one request against the key’s rate limit.

Name the skill in the message’s metadata.skill (or the request’s params.metadata.skill): sign, verify or disclosure-policy. Without it, a data part decides when it is unambiguous (asset_id or the sign flags mean sign, object_id means verify, jurisdiction or content_type mean disclosure-policy); otherwise the call is refused with -32602 (SKILL_REQUIRED).

The message’s messageId is its retry identity, like Idempotency-Key in REST: sending the same message again returns the task it already created instead of signing or verifying twice.

{
"jsonrpc": "2.0",
"id": "1",
"method": "SendMessage",
"params": {
"message": {
"messageId": "sign-7f3a",
"role": "ROLE_USER",
"metadata": { "skill": "sign" },
"parts": [
{ "data": { "asset_id": "ast_abcdefghijklmnop", "watermark": false, "fingerprint": true } }
]
},
"configuration": { "returnImmediately": true }
}
}

The data part is a POST /v1/sign body: watermark, fingerprint, ai_declaration, watermark_algorithm, watermark_strength, fingerprint_algorithms, creator_id and template_id behave exactly as there, and the same combinations are refused (an AI declaration is sign-only: watermark: false). Instead of asset_id you can send the file inline as a file part (raw with mediaType in 1.0; kind: "file" with bytes and mimeType in 0.3), at most 25 MB; the API then uploads it through the ordinary upload lifecycle. Upload a larger file with POST /v1/uploads and send its asset_id. A file part that only points at a URL is refused with -32005: the API does not fetch remote files.

The answer is a task whose id is the sign job (job_…). Poll it with GetTask. When it completes, its artifact carries the signed file as a time-limited download link (needs assets:read; otherwise fetch it with POST /v1/assets/{asset_id}/download) and a data part with the job, asset, publication, receipt and verification evidence ids.

A blocking send (the 1.0 default; blocking: true in 0.3) waits up to ten seconds for the job to finish, then returns the task as it stands; poll from there. Signing on the A2A endpoint always runs on the standard pipeline.

Send the file inline, or a data part with a stored object_id (aob_…; and manifest_object_id for a sidecar manifest). The task is the verification (vjob_…). When it completes, its artifact states the file’s declared origin only: the Content Credentials status (valid, valid_untrusted, invalid or absent) and what the manifest declares (signed_by, claim_generator, signed_at, ai_generated, digital_source_type, software_agent, actions), with this statement:

This describes what the file declares about its origin and whether this provider’s system has a record of it. It is not a judgement of whether the content is real.

A verification runs to completion; CancelTask on it is -32002.

{
"jsonrpc": "2.0",
"id": "2",
"method": "SendMessage",
"params": {
"message": {
"messageId": "policy-1",
"role": "ROLE_USER",
"parts": [{ "data": { "jurisdiction": "EU", "content_type": "image", "locale": "de" } }]
}
}
}

jurisdiction is one of EU, US-CA, US-ME, US-NH, US-NY, US-UT, IN, CN or KR; content_type is image, video, audio, text or interaction (a chatbot or assistant). A plain text question naming both also works. The answer is a message, not a task: a text summary and a data part with each matching rule’s provision, trigger, timing, form and notice text, from the published disclosure policy. The rule text is a draft pending legal review, and showing a notice does not by itself satisfy any law.

Refusals the REST API would answer with a reason code come back as JSON-RPC errors carrying that code: in 1.0, error.data is a list with one google.rpc.ErrorInfo whose reason is the code (for example JOB_POLICY_REJECTED) and whose metadata holds detail_code, field_details and retryable; in 0.3, error.data is an object with the same fields.

Code Meaning
-32700 The body is not JSON.
-32600 Not a single JSON-RPC 2.0 request (batches are not accepted).
-32601 Unknown method.
-32602 Invalid parameters, or a refusal from the REST service (see data).
-32603 The service is temporarily unavailable (retryable in data).
-32001 No such task for this account.
-32002 The task has ended, or is a verification.
-32003 Push notifications are not offered.
-32004 Streaming, resubscription and task listing are not offered.
-32005 A file given only by URL; send the bytes or an id.
-32007 No authenticated extended card.
-32009 Unsupported A2A-Version.

Authentication (401) and missing scopes (403) are HTTP statuses, not JSON-RPC errors.

A task is a view of a durable sign job or verification: GetTask reads it as the REST API does, under your account only, and another account’s id is -32001. The task’s contextId is its own id. History is not kept (historyLength is ignored), and there is no task list: list your jobs with the REST API.