Hannah API: developer sandbox

Hannah is a governed agent, not a chat box. Every call runs five layers server side: authority and consent, memory, tools, the channel, and the lifecycle clocks. You send a buyer message, you get back what Hannah did and why. A sandbox key points at a fictional dealership, so you can build all day without touching a real customer.

1. Get a key

Keys are issued by Quantum, one per integration. A sandbox key starts with qk_test_ and a live key starts with qk_live_. The key is shown once. Send it as a bearer token on every call.

Authorization: Bearer qk_test_...

2. Check the wire

curl https://automotivesalesintelligence.com/api/public/hannah/health \
  -H "Authorization: Bearer qk_test_..."

-> { "ok": true, "environment": "sandbox", "allowedChannels": ["web_chat","sms","email"] }

3. Find something to talk to

The sandbox has a fixed cast of buyers. Ask for them instead of hard coding ids.

curl https://automotivesalesintelligence.com/api/public/hannah/sandbox \
  -H "Authorization: Bearer qk_test_..."

-> { "ok": true, "opportunities": [ { "opportunityId": "...", "buyer": "Marcus Ellery", "vehicle": "2024 Chevrolet Silverado 1500", "stage": "new_lead" } ] }

4. Send a buyer message

curl -X POST https://automotivesalesintelligence.com/api/public/hannah/turn \
  -H "Authorization: Bearer qk_test_..." \
  -H "Content-Type: application/json" \
  -d '{
    "opportunityId": "<from step 3>",
    "channel": "web_chat",
    "text": "Is the blue one still available?"
  }'

-> 200
{
  "ok": true,
  "environment": "sandbox",
  "conversationId": "uuid",
  "status": "sent | held_for_approval | denied | escalated",
  "reply": "...",
  "reason": "why Hannah did that",
  "policyEffect": "allow | deny | require_approval",
  "confidence": 0.82,
  "correlationId": "uuid"
}

Keep the conversationId and send it back on the next message so the thread stays together. A denied or held answer is a normal, expected outcome, not an error: read reason and show it to your operator.

5. Push events instead of asking

If your system already owns the inbox, push what happened and let Hannah react.

POST https://automotivesalesintelligence.com/api/public/hannah/events
{ "type": "buyer.message", "opportunityId": "uuid", "channel": "sms", "text": "..." }
{ "type": "lead.created", "opportunityId": "uuid", "payload": { } }

6. Receive what Hannah does

Give Quantum an HTTPS endpoint and every finished turn is posted to it, signed so you can prove it came from us. Verify before you trust the body.

Headers
  x-quantum-event: turn.completed | turn.escalated
  x-quantum-signature: hex HMAC-SHA256 of the raw body, using your signing secret

Node
  const expected = crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
  const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));

Answer 200 within 10 seconds. Retries are automatic and duplicates are possible,
so treat the correlationId as the idempotency key.

Rules of the road

  • Sandbox keys can only see the sandbox dealership. Ids from anywhere else return 404.
  • No message from the sandbox ever reaches a phone or an inbox. Delivery is simulated and logged.
  • Every key has a daily call cap. Over it, you get 429 with the count.
  • 401 means the key is missing or unknown, 403 means revoked, expired or a channel you are not allowed on.
  • The sandbox can be reset at any time, so do not store its ids permanently.

Machine readable spec: https://automotivesalesintelligence.com/api/public/hannah/openapi