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"
}| Field | Type | Required | Description |
|---|---|---|---|
apiKey | string | ✅ | Your API key (prefix hs_) |
consumerSignPubKey | string | ✅ | Ed25519 signing public key (PEM) |
consumerEncryptPubKey | string | ✅ | X25519 encryption public key (PEM) |
messages | array | ✅ | Conversation messages (last 10 kept) |
question | string | ❌ | Specific question for the expert |
plaintext | string | ❌ | Plaintext 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
| Status | Meaning |
|---|---|
400 | Missing required fields |
401 | Invalid API key (must start with hs_) |
413 | Request body too large (max 1MB) |
429 | Rate 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 keysnew_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
| Scope | Limit |
|---|---|
| API v1 general | 30 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.