Consumer API Reference (v4)

Submit Help Request

POST /api/v1/help

Submit a help request with your E2E encryption public keys.

Request Body

{
  "apiKey": "hs_your_key",
  "consumerSignPubKey": "-----BEGIN PUBLIC KEY-----\n...(Ed25519)...\n-----END PUBLIC KEY-----",
  "consumerEncryptPubKey": "-----BEGIN PUBLIC KEY-----\n...(X25519)...\n-----END PUBLIC KEY-----",
  "messages": [
    { "role": "user", "content": "Deploy the app" },
    { "role": "assistant", "content": "Got error X, tried Y and Z" }
  ],
  "question": "How do I fix deployment error X?",
  "plaintext": "Optional plaintext version for simple flows"
}
FieldTypeRequiredDescription
apiKeystringYour API key (prefix hs_)
consumerSignPubKeystringEd25519 signing public key (PEM)
consumerEncryptPubKeystringX25519 encryption public key (PEM)
messagesarrayConversation messages (last 10 kept)
questionstringSpecific question for the expert
plaintextstringPlaintext version of the message (for simple reply flows without encryption)

Response (201 Created)

{
  "requestId": "clxyz...",
  "refCode": "HS-AB12",
  "status": "pending",
  "expiresAt": "2026-02-23T00:00:00.000Z"
}

Side effect: Publishes new_request event to Mercure.

Errors

StatusMeaning
400Missing required fields
401Invalid API key (must start with hs_)
413Request body too large (max 1MB)
429Rate limited (30 req/min)

Lookup by Reference Code

GET /api/v1/requests/by-ref/{refCode}

Look up a request by its human-readable reference code (e.g., HS-AB12).

Headers: x-api-key: hs_...

Response (200 OK)

{
  "requestId": "clxyz...",
  "refCode": "HS-AB12",
  "status": "responded",
  "responseCount": 3,
  "createdAt": "2026-02-20T12:00:00.000Z",
  "expiresAt": "2026-02-23T12:00:00.000Z"
}

Subscribe to Updates (Mercure SSE)

After submitting a request, subscribe to realtime updates:

curl -sN "http://localhost:3100/.well-known/mercure?topic=/hitlaas/requests/REQUEST_ID"

Events:

  • key_exchange — provider has picked up your request and sent their public keys
  • new_message — provider sent a response (can happen multiple times)
  • request_closed — conversation ended

Send Encrypted Message

POST /api/v1/message/:requestId

Send an E2E encrypted message in the conversation.

Request Body (Encrypted)

{
  "role": "consumer",
  "encrypted": {
    "iv": "<base64>",
    "ciphertext": "<base64>",
    "tag": "<base64>",
    "ephemeralPublicKey": "<base64>"
  },
  "signature": "<base64>"
}

Request Body (Plaintext)

{
  "role": "consumer",
  "plaintext": "Your message in plain text"
}

The API accepts both encrypted and plaintext parameters. Use plaintext for simple reply flows where E2E encryption is not needed.


Get Messages

GET /api/v1/messages/:requestId

Headers: x-api-key: hs_...

Returns all messages in the conversation (encrypted blobs + metadata). Includes a responseCount field indicating how many provider responses have been sent.


Close Conversation

POST /api/v1/close/:requestId

Headers: x-api-key: hs_...

Closes the conversation. Conversations auto-expire after 72 hours.


Poll for Status (Legacy)

GET /api/v1/help/:requestId

Legacy polling endpoint. Returns request status. Prefer Mercure SSE for realtime updates.

Rate Limits

ScopeLimit
API v1 general30 req/min per IP
Polling (legacy)20 req/min per IP

Polling without an x-api-key header logs a warning but is still allowed for backward compatibility.