Skip to main content
The sandbox is a live Parmana server you can call right now. It runs the same code as production, with its own database, its own signing keys and its own approver, and every check production runs is on. Its one action, sandbox:receipt, is released to an endpoint that acts on nothing and answers with a receipt. So you see the whole path for real: the policy, the human approval, the signed authorization, the release to an external endpoint, and a signed record you can verify yourself.
Everything you send to the sandbox is visible to every other visitor, because everyone shares one demo key. Send only demo text. Never send personal data, real order numbers or secrets.

The sandbox at a glance

Try it in your browser

Every page in the REST API tab has a Try it panel. It calls the sandbox first, with the demo key already filled in. Start with these, in order:
  1. Get the caller: who the demo key is.
  2. Get the policy in effect with capability sandbox:receipt: what a request must carry.
  3. Get a demo approval: a signed approval for a target you choose.
  4. Execute a transaction: your request, with that approval in signals.approvalArtifact.
The scripts below do the same in one run, so you can see every step together.

Run the whole flow

Each script does seven steps: who am I, what must a request carry, send with no approval (refused), get a demo approval, send with it (approved, released and signed), verify the signed record, send the same approval again (refused). TypeScript and Python use the published SDKs (npm install @parmana/sdk, pip install "parmana[verify]" requests). The cURL and PowerShell versions need nothing installed. Each script was run against the live sandbox before it was published here; the files are in examples/sandbox-playground. For TypeScript and Python, set the key first: export PARMANA_API_KEY=2VfYWCzt_cBAPK-8uufX6ordfY2JuQhFPsohuEumKME (macOS, Linux) or $env:PARMANA_API_KEY = "2VfYWCzt_cBAPK-8uufX6ordfY2JuQhFPsohuEumKME" (PowerShell). Then run npx tsx playground.ts or python playground.py.

What each step returns

Every response below is real, captured from the sandbox on 2026-10-02.
1

1. Who am I: GET /callers/me

200
The demo key may ask for sandbox:receipt and nothing else, and only as sandbox-visitor.
2

2. What must a request carry: GET /policies/in-effect

Query: capability=sandbox:receipt.
200
Read it like this. policy is the name and version your request must name. signals.facts are the facts the policy reads, and signals.schema gives their types. signals.bound says note must equal parameters.note, so you cannot declare one note and release another. signals.approval says receiptApproved is true only with a signed approval whose resourceId is the request’s target. The server checks that itself, so declaring true without one is refused.
3

3. Send with no approval: refused

403
Nothing was released.
4

4. Get a demo approval: POST /sandbox/approvals

Body: { "capability": "sandbox:receipt", "resourceId": "your target" }
201
In production this object is made by a person on their own machine with their own private key (Human approval). Here the sandbox’s demo approver signs it for anyone. The server checks it exactly as it checks a real one: a trusted approver key, the signature, the capability, the resourceId against your target, the expiry, and that it was never used before.
5

5. Send with the approval: approved, released, signed

Put the whole approval object in signals.approvalArtifact and set signals.receiptApproved to true. The response is the signed Execution Trust Record. Abridged here; the full record also carries the request, the signed authorization and the chain hashes:
200 (abridged)
responseSummary is what the receipt endpoint answered after it verified Parmana’s signed release.
6

6. Verify the record yourself

Fetch the sandbox’s public key from GET /keys/default (no key needed) and check the record offline, with no further call to the server:
Offline verification result
TypeScript: verifyExecutionTrustRecordOffline(record, { default: pem }). Python: verify_execution_trust_record_offline(raw_record, {"default": pem}), with the record as plain JSON (see the note below). Change one character of the record and valid becomes false. See Verify independently.
7

7. Send the same approval again: refused

403
An approval is accepted once. A second request with it is refused, even for the same target.
Python SDK 1.4.0: pass the record to verify_execution_trust_record_offline as the plain JSON the server sent, for example from GET /trust-records/ {businessTransactionId}. The decoded model that client.execute() and client.trust_record() return drops a null previousChainHash, so verifying the model reports a hash mismatch although the record is intact. The TypeScript SDK is not affected, and the next Python release fixes it.

Every refusal you can meet

All real, from the sandbox. Every code is explained in the Error catalog.

Rules for a raw HTTP request

The SDK builders (createBusinessTransaction, create_business_transaction) follow these for you. Without an SDK:
  • businessTransactionId is a new UUID for every request, and metadata.businessTransactionId is the same UUID.
  • intent.action is sandbox:receipt. intent.target is any text you choose, and the approval’s resourceId must equal it.
  • intent.parameters.note and signals.note are the same text.
  • policy is { "name": "sandbox-receipt", "version": "1.0.0", "schemaVersion": "1.0.0" }, as GET /policies/in-effect says.
  • authority.principalId is sandbox-visitor. authorityId, authorizationId and intentId are any text, linked as in the scripts.
  • signals.approvalArtifact is the approval object exactly as POST /sandbox/approvals returned it, within 5 minutes.

From the sandbox to your own system

For AI agents

To test an integration before you have your own deployment: base URL https://parmana-sandbox.vercel.app, bearer key 2VfYWCzt_cBAPK-8uufX6ordfY2JuQhFPsohuEumKME, capability sandbox:receipt. Read GET /policies/in-effect?capability=sandbox:receipt first and build the request from its answer. Get the approval from POST /sandbox/approvals with resourceId equal to your target. Send POST /execute with a client timeout of 120 seconds. Treat 403 POLICY_DENIED as a final refusal, not something to retry. The full guide is Integrate Parmana, and the machine readable spec is openapi.yaml.