Soren

An agent cannot vouch for itself

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.

/v1/attestation — proof it existed by then — $0.02

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>&note=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.

What it proves, and what it does not

ProvesDoes 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.

/v1/secret — one-read handoff — $0.03

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.

Also here: signed oracles over public-domain sources, a free x402 conformance suite, and cross-store game pricing with real observed history.