Skip to content

Five minutes to a verified evidence record

Copy a key, seal a record, read the receipt, see it in the control plane, verify it offline. Everything on this page runs against the live API.

What this is for

Your AI acts. This is the proof of what happened.

Your AI drafts and does consequential things — files documents, sends client work, releases billing entries. Planisphere is the missing evidence layer around those acts: gate holds a consequential act until a human signs off, the act and the sign-off seal into a tamper-evident receipt, and anyone — an auditor, a client, opposing counsel — can verify that receipt forever, offline, without trusting you or us. You wrap one boundary in your code; you get an audit trail of your AI's actions as a side effect.

Impatient? Jump to the complete example — a 50-line agent wrapper you can run in two minutes.

Step 1

Get your key

Get a free test key in the console — work email + organization, no card, minted in place. The key starts with ps_test_, is shown once, and is limited to one sandbox per email (a repeat email returns 409 with an upgrade pointer). Free allowance: 100 sealed actions.

Step 2

Make your first call

Auth is a single header: X-Planisphere-Key (a Authorization: Bearer token also works). Gate an action — the API decides, and seals the decision as evidence:

Note

Pack context has a named envelope: the law pack reads law_context (matter_band, tool_id, doctrine_anchor…); other packs read context + action_type. GET /v1/packs lists each pack's exact required fields.

curl -sS -X POST https://api.planisphere.ooo/v1/gate \
  -H "X-Planisphere-Key: ps_test_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"pack":"law","proposed_action":"File this motion with the court."}'

Note

Every pack and its exact request shapes are machine-readable at GET /v1/packs — the honest source for what each gate accepts.

Prefer an SDK?

Official thin clients (no runtime dependencies), live on PyPI and npm — see the SDK guide.

pip install planisphere
from planisphere_sdk import Planisphere

ps = Planisphere(api_key="ps_test_...")
decision = ps.gate(
    pack="law",
    proposed_action="File this motion with the court.",
    idempotency_key="run-42",  # safe to retry - returns the original result, meters once
)
npm install planisphere-sdk
import { Planisphere } from "planisphere-sdk";

const ps = new Planisphere("ps_test_...");  // key is positional, not an options object

const decision = await ps.gate({
  pack: "law",
  proposed_action: "File this motion with the court.",
  law_context: { matter_band: "litigation", tool_id: "harvey" },
  idempotencyKey: "run-42",  // safe to retry - returns the original result, meters once
});

// Record the human call on a routed action, then prove the receipt:
// every gate/review/record response carries an in-band verify_request.
await ps.recordReview({
  pack: "law",
  action_key: decision.action_key,
  reviewer: "partner@firm.example",
  decision: "approved",
});
const check = await ps.verify(decision.verify_request);  // { verified: true, ... }

Step 3

Read the response

You get a decision (allow, needs_review, or blocked), an action_key, and an evidence_packet — the packet is your sealed record: hash-committed, signed, and independently checkable.

Note

A fresh sandbox typically returns needs_review — the gate routed the action to human review, and the record sealed either way. That routing is the product working; allow comes once your tenant policy allowlists the action type.

Note

Sandbox receipts sign with a public, reproducible dev key (dev_key:true) and a different public key than production — they are provably non-production and must never be presented as production evidence. Production keys sign with a managed, non-exportable key.

Also: attest completed acts

Record what already happened

The gate asks permission before an act. When the act is already done — an output was marked, a disclosure was shown — attest it with POST /v1/record using a record-verb pack event (the eu-ai-act and ca-sb942 duty vocabularies): terminal "status": "recorded", a sealed evidence packet, no review routing. Send hash commitments, not content (POST /v1/record/batch takes an array for volume):

curl -sS -X POST https://api.planisphere.ooo/v1/record \
  -H "X-Planisphere-Key: ps_test_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"pack":"eu-ai-act","event_type":"output_marked",
       "occurred_at":"2026-07-22T17:03:11Z",
       "source_payload":{"content_sha256":"<sha256-of-your-output>"},
       "metadata":{"marking_techniques":["signed_metadata","imperceptible_watermark"],
                   "marking_payload_ids":["c2pa:manifest-1"],
                   "model":"imagegen-large","model_version":"4.2.0",
                   "detection_check_result":"pass"}}'

Note

Payload retention: Planisphere stores hash commitments only — retain your payload to re-prove WHAT was recorded. Every record response repeats this in its payload_retention field.

Note

Batch shape: POST /v1/record/batch takes a bare JSON array of record objects — each item is the exact single-record body, no wrapper key:

curl -sS -X POST https://api.planisphere.ooo/v1/record/batch \
  -H "X-Planisphere-Key: ps_test_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '[{"pack":"eu-ai-act","event_type":"output_marked", ...},
       {"pack":"eu-ai-act","event_type":"disclosure_rendered", ...}]'

Step 4

See it in the control plane

Open the control plane and paste your key into the key field (replace the placeholder value). Run gates, browse receipts, manage keys, and export your audit trail. From code, fetch any record by its key:

curl -sS https://api.planisphere.ooo/v1/records/ACTION_KEY \
  -H "X-Planisphere-Key: ps_test_YOUR_KEY"

Note

Records are fetched by action_key — hold onto the keys your gates return. For a period, export sealed records with GET /tenant/exports/records?from=&to=&cursor= (filters are always applied — unknown query parameters are rejected, never silently ignored), then POST the action keys to /evidence-packets/seal-bundle/batch as a wrapped object — {"action_keys":["law:...","eu-ai-act:..."]}, unlike the bare array /v1/record/batch takes — for one offline-verifiable evidence package.

Note

GET /v1/records/ACTION_KEY/inclusion-proof returns the canonical roll-up inclusion proof for any record. Records anchor into a canonical epoch on the hourly roll-up, so a record sealed moments ago returns an honest 409 until the next epoch — the body carries epoch_cadence, a next_epoch_hint, and an estimated_next_epoch_at timestamp telling you when to retry.

Step 5

Verify the receipt — without trusting us

Combine your evidence_packet with three fields from the gate response (decision, payload_hash, review_route) and POST to /v1/verify:

curl -sS -X POST https://api.planisphere.ooo/v1/gate \
  -H "X-Planisphere-Key: ps_test_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"pack":"law","proposed_action":"File this motion with the court."}' > /tmp/gate.json

python3 -c 'import json; d=json.load(open("/tmp/gate.json")); p=d["evidence_packet"]; \
print(json.dumps({"action_key":p["action_key"],"href":p["href"],"status":p["status"], \
"packet_hash":p["packet_hash"],"seal":p["seal"],"decision":d["decision"], \
"payload_hash":d["payload_hash"],"review_route":d.get("review_route")}))' > /tmp/verify.json

curl -sS -X POST https://api.planisphere.ooo/v1/verify \
  -H "X-Planisphere-Key: ps_test_YOUR_KEY" \
  -H "Content-Type: application/json" -d @/tmp/verify.json

Expect "verified": true with every seal check itemized — signature, hashes, and key identity, each recomputed.

For fully offline verification: GET /v1/verify/kit returns the recipe (fields, hashes, and checks — no key material required), and planisphere.ooo/verify verifies seal-bundle files entirely in your browser; the record data never leaves the tab. Don't trust us — re-check it yourself.

Put it together

A complete example: an AI assistant that releases drafted work

The scenario every team recognizes: an agent drafts client-facing output and releases it. This file adds the evidence layer — the release is gated, the human sign-off is sealed, and the receipt verifies. Download it (quickstart_agent.py), run it with your test key, and read the four lines it prints:

"""An AI assistant that releases drafted work — with evidence.

Scenario: your AI drafts client-facing output (a filing, an email, a billing
entry). Today it just... goes out. This example adds the missing layer: every
release is gated, human sign-off is sealed, and anyone can verify the record
later without trusting you or Planisphere.

Run it:  pip install planisphere && python quickstart_agent.py ps_test_YOUR_KEY
"""

import sys

from planisphere_sdk import Planisphere, PlanisphereNeedsReview


def release_draft(ps: Planisphere, draft_id: str, summary: str) -> dict:
    """The one change to your agent: gate the release instead of just doing it."""
    try:
        decision = ps.require_allow(
            pack="law",
            proposed_action=f"Release drafted document {draft_id}: {summary}",
            surface="agent",
            source_key=f"agent:draft:{draft_id}",
            law_context={"matter_band": "active-litigation", "tool_id": "my-agent"},
            idempotency_key=f"release-{draft_id}",  # retries never double-bill
        )
    except PlanisphereNeedsReview as exc:
        decision = exc.decision
        print(f"  paused: routed to {decision['review_route']} — a human decides")
        # In production you'd stop here and resume on webhook/poll. For the
        # demo, the supervising human approves right now:
        ps.record_review(pack="law", action_key=decision["action_key"],
                         reviewer="supervising-partner", decision="approved")
        print("  approved by supervising-partner — sign-off sealed")
    return decision


def main() -> int:
    ps = Planisphere(sys.argv[1])  # key is positional
    decision = release_draft(ps, "draft-014", "Motion to compel, Johnson v. Dunn")

    # The part your auditor cares about: the receipt proves itself.
    verdict = ps.verify(decision["verify_request"])
    print(f"  receipt verified: {verdict['verified']} "
          f"(key_status: {verdict['key_status']})")
    print(f"  keep this action_key for your records: {decision['action_key']}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())

Expected output (this exact run was executed against the live API before this page shipped):

  paused: routed to legal-ops:policy-review — a human decides
  approved by supervising-partner — sign-off sealed
  receipt verified: True (key_status: sandbox)
  keep this action_key for your records: law:proposed-action:…

Note

In production the agent stops at "paused" and resumes when your reviewer approves — via the webhook events or by polling the record. The demo approves inline so you can see the whole loop in one run. Same pattern in TypeScript: require_allow → catch PlanisphereNeedsReviewrecordReviewverify.

When you're ready

Go to production

Track your allowance anytime: GET /tenant/usage returns actions used, your cap, what remains, and a warning as you approach it. Upgrade with the same email at signup — paid checkout rotates your sandbox tenant to a ps_live_ key and preserves its history. Usage-metered: 1¢ per credit, capped per action.

Go deeper

Integration guides

Live guides for the SDKs, Python/JavaScript middleware, LangChain, OpenAI Agents, Slack, ServiceNow, and signed event webhooks.

Next: start with the Client SDKs — the thinnest path from this page to production code.


U.S. patents pending · © 2026 PlanisphereUS, Corp. · Delaware C Corp · API reference · Pricing · Integration guides