Ask me anything

Hamburger

Errors

How the API signals problems, how to parse them, and how to react. All error responses are JSON unless the connection fails.

Structure

Every unsuccessful JSON response uses a minimal envelope:

1{ 2 "error": "INVALID_CONTENT", 3 "message": "Missing content field" 4}
  • error: Stable machine-readable code.
  • message: Human-readable explanation (not for programmatic branching).

Categories

  • Validation (400): Malformed JSON, missing fields, type mismatches, mutually exclusive / required groups.
  • Authentication (400 / 401): Missing agent key (400) vs invalid (rotated) key (401).
  • Balance (402): Insufficient balance to perform the action.
  • Internal (500): Transient platform issue.
  • External (502): Upstream model / dependency failure.

Balance headers behavior

On success Message / Sort / Rewrite / Should-engage include usage headers (cost, balance). They are omitted on 400 validation errors and not returned by Limits at all.
See Limits.

Common error codes

HTTPErrorMeaningAction
400INVALID_JSONBody isn't valid JSON.Fix JSON formatting.
400INVALID_CONTENT / INVALID_TEXT / INVALID_ARRAYRequired field missing or empty.Provide a valid non-empty field.
400INVALID_HISTORY / INVALID_INSTRUCTIONS / INVALID_CONTEXTField present but wrong type.Correct the type or omit the field.
400MISSING_AGENT_KEY / INVALID_AGENT_KEYAuthorization header missing.Add agent key.
401UNAUTHORIZEDKey not recognized (revoked / rotated).Use current active key.
402INSUFFICIENT_BALANCEBalance is exhausted.Add funds.
500INTERNALPlatform processing error.Retry / contact support.
502EXTERNAL_SERVICEUpstream dependency issue.Retry; transient.

Handling strategy

  • 400: Surface validation feedback directly; client can correct without retry loops.
  • 401: Rotate key.
  • 402: Back off; optionally poll Limits endpoint; avoid aggressive retry.
  • 500 / 502: Implement exponential backoff with jitter; cap attempts.

Example handler

Minimal pattern for differentiating recovery paths in JavaScript:

1async function callMessage(content){ 2 const res = await fetch('https://heylock.dev/api/v1/message', { 3 method: 'POST', 4 headers: { 5 'Authorization': process.env.HEYLOCK_AGENT_KEY, 6 'Content-Type': 'application/json' 7 }, 8 body: JSON.stringify({ content }) 9 }); 10 11 if(response.ok){ 12 const data = await response.json(); 13 return { ok: true, message: data.message }; 14 } 15 16 let payload = null; 17 try { payload = await response.json(); } catch { /* Non-JSON error */ } 18 19 const code = response.status; 20 const error = payload?.error; 21 22 switch(code){ 23 case 400: 24 // Validation – fix request. 25 throw new Error(error || 'Bad Request'); 26 case 401: 27 // Invalid / revoked key. 28 throw new Error('Auth failed – verify agent key'); 29 case 402: 30 // Balance depletion – add funds or backoff. 31 throw new Error('Balance exhausted – add funds or backoff'); 32 case 500: 33 case 502: 34 // Transient. 35 throw new Error('Temporary issue – retry later'); 36 default: 37 throw new Error('Unexpected response'); 38 } 39}

Route differences

  • Should-Engage returns fallback object (HTTP 200) instead of an error when model output can't be parsed.
  • Message streaming: parse line-by-line; each line maintains the same message/done shape—errors still use standard JSON.
  • Limits never decrements usage.

Testing your handling

  • Send deliberately malformed JSON to trigger INVALID_JSON.
  • Omit required fields: e.g. remove content in Message or array in Sort.
  • Rotate an agent key then call an endpoint with the old one for a 401.
  • Simulate balance exhaustion by looping valid calls until a 402 (observe headers decrement each success).

Explore

API - Errors | Heylock