Developer documentation

Steady North Decision API

Authenticate with an organization API key: Authorization: Bearer sn_sbx_…. Keys are created in the dashboard, scoped, and shown once. Every decision returns a receipt id; receipts are independently verifiable. This demo environment ships a seeded key for the Lakeshore demo org so the examples below work as-is:

sn_sbx_2f7c9a41d8e3b6055f1a4c2d9b8e7f60a3c1d5b2

POST/api/v1/evaluatescope: certbridge:write | agebridge:evaluate

The generic decision endpoint — module, subject, jurisdictions, normalized context, and evidence in; decision, requirements, missing evidence, and receipt out.

curl -s http://localhost:3960/api/v1/evaluate \
  -H "Authorization: Bearer sn_sbx_2f7c9a41d8e3b6055f1a4c2d9b8e7f60a3c1d5b2" \
  -H "content-type: application/json" \
  -d '{
    "module": "CERTBRIDGE",
    "subjectLabel": "Sample imported toy",
    "jurisdictions": ["US"],
    "context": { "product": { "category": "TOY", "isChildrensProduct": true,
                 "imported": true, "recallStatus": "NONE" } },
    "evidence": [
      { "key": "field:manufacturer_name", "value": "Acme Toys" },
      { "key": "citations:present", "value": "ASTM F963-23" }
    ]
  }'

POST/api/v1/age/evaluatescope: agebridge:evaluate

AgeBridge wrapper — speak in app terms. Age signals are ranges, never birth dates. The example uses an inline app; registered apps pass appId.

curl -s http://localhost:3960/api/v1/age/evaluate \
  -H "Authorization: Bearer <loopline key>" \
  -H "content-type: application/json" \
  -d '{
    "app": { "name": "MyApp", "category": "SOCIAL", "minorsAllowed": true },
    "platform": "IOS",
    "jurisdictions": ["US", "US-TX"],
    "feature": { "key": "DIRECT_MESSAGING", "capabilities": ["MESSAGING","SOCIAL"] },
    "ageSignal": "13_15",
    "parentApprovalState": "NOT_REQUESTED"
  }'

→ { "decision": "REQUIRE_PARENT_APPROVAL", "rules": [...], "receiptId": "SN-RCP-…" }

GET/api/v1/receipts/{id}scope: receipts:read (org receipts)

Fetch a receipt with its canonical payload and a fresh verification. Receipts from the public checkers are readable without a key.

POST/api/v1/receipts/verify

Answers one question: has this record changed since it was issued?

curl -s http://localhost:3960/api/v1/receipts/verify \
  -H "content-type: application/json" \
  -d '{ "receiptId": "SN-RCP-XXXXXXXXXXXX" }'

→ { "valid": true, "checks": [
     { "name": "Payload hash", "ok": true },
     { "name": "Signature", "ok": true },
     { "name": "Receipt id embedded in payload", "ok": true } ] }

Webhooks

Register endpoints in the dashboard. Deliveries POST JSON with headers x-sn-event, x-sn-delivery, and x-sn-signature (t=…,v1=HMAC_SHA256(secret, `${t}.${body}`)). Failed deliveries retry with exponential backoff (1m → 64m, 5 attempts). Events: decision.created, decision.changed, filing.status_changed, recall.detected, parent_approval_required, product.action_required, rule.changed.

SDK surface (planned): AgeBridge Swift / Kotlin adapters normalize native platform signals (Apple declared age range, Google Play age signals) into the ageSignal vocabulary client-side, then call /v1/age/evaluate. Native calls are never faked from the server.