An agent that signs its own output has proved nothing to anybody. It holds the key. It could have signed anything, at any time, and said it was yesterday. Self-attestation is a statement about intent, not evidence.
These two endpoints exist for the cases where that matters — where a third party with no stake is worth more than doing it yourself.
Send a SHA-256. Get back a signed, timestamped statement that Soren — not you — saw that hash at that moment.
GET /v1/attestation?hash=<64 hex>¬e=model+output+v3
{
"hash": "6e7ac308…",
"observed_at": "2026-08-19T19:09:15.290Z",
"statement": "attestation|6e7ac308…|2026-08-19T19:09:15.290Z|model output v3",
"signature": { "algorithm": "Ed25519", "value": "…" }
}
We never see your content. You hash it yourself and send 64 characters. That is not a courtesy — it is the security property. There is nothing for us to leak, lose, or be compelled to produce, and no size limit to abuse.
| Proves | Does not prove |
|---|---|
This exact hash was presented to Soren no later than observed_at, and Soren says so independently. |
Not who created it. Not that anyone else saw it. Not that it is true, lawful or yours. Not that it existed earlier. Not a legal notarisation — Soren is not a notary. |
Verify by rebuilding statement from the returned fields and
checking the signature with the key at
/oracle-key.json. It composes with the
signed oracles: attest your own output, and the inputs
it was derived from, and you have a chain of custody a counterparty can
check without trusting either of you.
Two agents need to pass a credential and neither should hold it first. POST the secret, pay once, get a URL that works exactly one time.
POST /v1/secret?ttl=300 (body: the secret)
→ { "read_url": "https://soren.com/v1/secret/<token>",
"decryption_key": "<64 hex, returned once, never stored>",
"mode": "encrypted", "reads_allowed": 1 }
GET /v1/secret/<token> → ciphertext, then it is gone
GET /v1/secret/<token>?key=<hex> → we decrypt it for you (we see the key)
GET /v1/secret/<token> → 404, forever
What this does and does not guarantee. We generate a random
AES-256-GCM key, encrypt your bytes, store only the ciphertext, return the
key to you once and discard it. So a breach of our storage gives up
ciphertext — that is a property, not a promise. What no wording can
prove from outside is that we did not keep the key at the moment we
generated it, so we say it plainly instead of implying a guarantee we cannot
offer. If that matters, encrypt yourself and POST with
?blind=true: we store your blob untouched and generate no key
at all. Either way, do not store anything you could not rotate.
We deliberately did not add an “I certify I encrypted this” flag. It would shift no real liability — what matters is what we actually held, not what a caller promised — and it would change no behaviour, because a required flag is a thing everyone sets to make the request work. Worse, it would imply we had addressed the risk while leaving it exactly where it was. Encrypting the payload removes the risk; a checkbox only removes the appearance of it.