Sandbox
Test keys and expected verdicts, without talking to anybody. No sign-up of ours, no demo tenant, no mock.
Expected verdicts that are not ours
A sandbox whose expected answers we invented would be us grading our own homework: a mock agrees with our bug as readily as with our correctness. So this sandbox serves the conformance corpus of the public specification — real sealed files, generated from a public test key, whose expected verdict was decided in review from the specification and written down before the file was produced. The rule in that repository is explicit: never edit an expectation to make an implementation pass.
Every answer names the corpus it served: version, the SHA-256 of the manifest as committed, how many vectors the manifest declares and how many are served. A conformance claim that cannot name its corpus and its count is compatible with having checked nothing.
Use it
# 1. list what is served
curl -H "Authorization: Bearer $VCAP_SANDBOX_KEY" \
"$VCAP_API_BASE/api/v1/sandbox/vectors"
# 2. take one, with the verdict the specification expects
curl -H "Authorization: Bearer $VCAP_SANDBOX_KEY" \
"$VCAP_API_BASE/api/v1/sandbox/vectors/01-jpeg-sealed" > vector.json
# 3. feed the same bytes to the API, with the request the vector names
jq '{media_b64, sidecar_b64, c2pa_store_b64} + .verify | with_entries(select(.value != null))' vector.json \
| curl -X POST "$VCAP_API_BASE/api/v1/verify" \
-H "Authorization: Bearer $VCAP_SANDBOX_KEY" \
-H "Content-Type: application/json" --data-binary @-
Note the verify block: for a container vector it carries recompute_segments: true, because that is what the corpus means by that kind — the expectation is the one reached with each segment's content hash recomputed from the container you received. Leave it off and a video with a replaced frame reads frames_not_compared instead of tampered, and so does a genuine clip: a proof's own segment hashes prove that somebody signed some hashes, not that those are the frames in your hands. It is worth knowing which question you are asking in production too.
Step 3 is the one worth automating in your own suite. Compare outcome, labels and not_evaluated against the vector's expected — and do it against the offline verifier too, because that comparison is the one that does not involve us at all.
A test key cannot touch real data
The sandbox uses the confinement the tenancy already has: keys belong to exactly one of three fixed environments, and these endpoints answer keys of the sandbox environment only. A dev or production key is refused with 403 VCAP_SANDBOX_ONLY. There is no fourth environment and no "test mode" flag beside the key — a second way of saying the same thing is a second thing that can disagree.
Nor is there a sandbox account of ours to sign up for. The key comes from your own organization, issued in the console like any other. Fake accounts we created would be one more thing to keep in step with the real tenancy.
It is not free verification without end
Fetching a vector is a file read and costs nothing. The verification you then run is metered and held to your organization's monthly limits exactly like production's — limits are per organization, not per environment. That is deliberate: no new limit was invented for the sandbox, and none was waived for it.
What the sandbox does not serve, and why
The corpus is larger than what you can get here. Some vectors pin an injected input: the clock the verifier must pretend to have, the online revocation answer it must pretend to have fetched, what the chain must be said to have recorded. A running service has its own clock and its own trust set, so it cannot be asked those questions — sending its bytes would produce a legitimately different verdict, and a sandbox that papered over that would be a mock with extra steps. Others carry no file at all (canonicalization and segment-chain vectors are messages, not media), and one carries an empty file, which a request cannot: an upload is at least one byte, and an empty one is refused as a malformed request rather than read as a file.
Those vectors are declared rather than dropped: the listing says how many the manifest holds and how many are served, and the place to run the rest is the corpus runner in the specification repository, which is public and does not involve us.