Agents (A2A)
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.
Authenticate
Section titled “Authenticate”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.
Choose a skill
Section titled “Choose a skill”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.
Verify
Section titled “Verify”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.
Disclosure policy
Section titled “Disclosure policy”{ "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.
Errors
Section titled “Errors”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.
Tasks are views of jobs
Section titled “Tasks are views of jobs”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.