Ask me anything

Hamburger

Message

POST https://heylock.dev/api/v1/message

Send a message to your agent and receive a single response or a streaming sequence of chunks.
Use this for conversational interactions.

Authentication

Provide the agent key in the Authorization header. Raw key only (no Bearer prefix). See Authorization.

Request body

  • content (string, required): User's message. Non-empty.
  • history (array<{ content: string, role: "user" | "assistant" }>, optional): Prior turns to preserve conversation continuity.
  • context (string, optional): Additional situational data. Brief structured narrative.
  • stream (boolean, optional, default false): If true, returns incremental chunks until completion.

Behavior

  • If context is present it's prepended as a system message.
  • Validation rejects empty strings and malformed history entries.
  • When streaming, each line is a JSON object with partial text.
  • Final non-stream response contains a single message field.

Responses

Non-streaming

Returns JSON with the agent reply.

1{ 2 "message": "Hello. It's nice to meet you." 3}

Streaming

When stream is true, the body is a stream of newline-delimited JSON objects. Each chunk has a message fragment and done flag. The final object sets done true.

1{ "message": "Hello", "done":false } 2{ "message": " there", "done": false } 3{ "message": "!", "done": false } 4{ "message": "", "done": true }

Examples

Basic message:

1const response = await fetch('https://heylock.dev/api/v1/message', { 2 method: 'POST', 3 headers: { 4 'Authorization': process.env.HEYLOCK_AGENT_KEY, 5 'Content-Type': 'application/json' 6 }, 7 body: JSON.stringify({ 8 content: 'Hello there' 9 }) 10}); 11 12const data = await response.json(); 13console.log(data.message);

With prior history and context:

1const history = [ 2 { role: 'user', content: 'Hi' }, 3 { role: 'assistant', content: 'Hello! How can I help?' } 4]; 5 6const response = await fetch('https://heylock.dev/api/v1/message', { 7 method: 'POST', 8 headers: { 9 Authorization: process.env.HEYLOCK_AGENT_KEY, 10 'Content-Type': 'application/json' 11 }, 12 body: JSON.stringify({ 13 content: 'Can you remind me what you said?', 14 history, 15 context: 'User revisiting earlier answer.' 16 }) 17}); 18 19console.log(await response.json());

Streaming consumption in JavaScript:

1const response = await fetch('https://heylock.dev/api/v1/message', { 2 method: 'POST', 3 headers: { 4 Authorization: process.env.HEYLOCK_AGENT_KEY, 5 'Content-Type': 'application/json' 6 }, 7 body: JSON.stringify({ 8 content: 'Explain quantum tunneling simply', 9 stream: true 10 }) 11}); 12 13if (!response.body) throw new Error('Streaming not supported'); 14 15const reader = response.body.getReader(); 16const decoder = new TextDecoder(); 17let full = ''; 18 19while (true) { 20 const { value, done } = await reader.read(); 21 if (done) break; 22 23 const chunk = decoder.decode(value, { stream: true }); 24 25 for (const line of chunk.split('\n')) { 26 if (!line.trim()) continue; 27 28 try { 29 const obj = JSON.parse(line); 30 31 if (obj.message) { 32 full += obj.message; 33 process.stdout.write(obj.message); 34 } 35 36 if (obj.done) { 37 console.log('\n[done]'); 38 } 39 } catch (e) { 40 // Ignore partial/incomplete lines 41 } 42 } 43} 44 45console.log('Full message:', full);

Streaming with cURL (shows incremental JSON lines):

1curl -N -X POST https://heylock.dev/api/v1/message \ 2 -H 'Authorization: AGENT_KEY' \ 3 -H 'Content-Type: application/json' \ 4 -d '{ 5 "content": "Stream a short poetic line", 6 "stream": true 7 }'

Usage headers

Successful responses include usage headers (balance, cost. Not returned on 4xx validation errors. If remaining balance reaches zero, you'll get 402 status code.
See Limits for details.

1const response = await fetch('https://heylock.dev/api/v1/message', { 2 method: 'POST', 3 headers: { 4 Authorization: process.env.HEYLOCK_AGENT_KEY, 5 'Content-Type': 'application/json' 6 }, 7 body: JSON.stringify({ 8 content: 'Hello' 9 }) 10}); 11 12if (!response.ok) throw new Error('Request failed'); 13 14const balance = Number(response.headers.get('balance')); 15const cost = Number(response.headers.get('cost')); 16 17console.log({ balance, cost }); 18

Errors

Handle both validation and runtime failures. Error bodies contain error and message.

CodeErrorWhat it meansWhat to do
400INVALID_JSONBody isn't valid JSON.Check request body formatting.
400INVALID_CONTENTMissing or empty content.Provide a non-empty content string.
400INVALID_HISTORYhistory is not an array.Pass an array of valid message objects.
400INVALID_AGENT_KEYMissing Authorization header.Add your agent key to the header.
401UNAUTHORIZEDKey doesn't match an agent (rotated or revoked).Regenerate and use a valid agent key.
402INSUFFICIENT_BALANCEBalance exhausted.Add funds.
500INTERNALTemporary internal issue.Retry or contact support if persistent.
502EXTERNAL_SERVICETemporary external issue.Retry; usually resolves itself.
1{ 2 "error": "INSUFFICIENT_BALANCE", 3 "message": "Your account balance is zero or less. Please add funds to continue using the service." 4}

Common issues

  • Sending current user message inside history.
  • Parsing streaming chunks as a single JSON blob instead of line by line.
  • Assuming headers appear on validation 400 responses.
  • Rotating agent key and reusing the old one.

Explore