Tutorial

Quickstart

Receive a message and send a reply through the VoiceLayer API in about 10 minutes, without a real phone.

What you'll build

You pretend to be a customer who texts your business, read that message with the API, and answer it. Everything runs in test mode: no real phone, no carrier, no charge. When you are done, the same code works with a live key and real people.

Prerequisites

  • A VoiceLayer account. Sign in to the dashboard at vlayers.ai.
  • Nothing else: you don't need a phone number. Test mode gives your workspace a free test sender the first time it needs one.
  • One of: a terminal with curl, Node.js 18 or later, or Python 3.9 or later.

Step 1: Create a test key

A test key lets you use the real API without reaching a carrier or paying anything.

  1. In the dashboard, open Developers → API keys and choose Create key.
  2. For "What is this key for?", pick Your servers or the SDK.
  3. Set Environment to Testing. Test keys start with vl_test_.
  4. Copy the key. It is shown once.

Save it in your shell. Every example on this site reads it from VOICELAYER_API_KEY:

export VOICELAYER_API_KEY=vl_test_...

Step 2: Install an SDK (optional)

The examples come in curl, TypeScript and Python. Skip this step if you use curl.

The TypeScript examples are ES modules that use top-level await. In a new project, run npm pkg set type=module once, then save an example as example.ts and run it with npx tsx example.ts. The Python examples run with python example.py.

npm install @voicelayer/api

Step 3: Simulate a customer writing to you

POST /v1/test/inbound plays the part of a person texting your number from a reserved test address (+15550100 to +15550199). It opens a conversation between that person and your workspace.

curl -X POST https://api.vlayers.ai/v1/test/inbound \
  -H "Authorization: Bearer $VOICELAYER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "channel": "sms",
    "from": "+15550123",
    "text": "Do you have a table for two tonight?"
  }'
Response · 202
{
  "conversationId": "0192f5b0-7c3e-7a51-9b2d-4e8f1a6c3d21",
  "messageId": "0192f5b0-8d1f-7b62-8c3e-5f9a2b7d4e32",
  "test": true
}

Copy the conversationId and save it. The next steps read it:

export CONVERSATION_ID=<the conversationId you got>

Step 4: Read the message

GET /v1/conversations/{id}/messages returns the conversation's messages. before=end asks for the newest page.

curl "https://api.vlayers.ai/v1/conversations/$CONVERSATION_ID/messages?before=end&limit=20" \
  -H "Authorization: Bearer $VOICELAYER_API_KEY"

You see the inbound message: "Do you have a table for two tonight?"

If the number's agent is set to answer, it replies too (in test mode, at no cost), so you may see its answer here. To answer only from your code, hand the conversation to your code.

Step 5: Reply

POST /v1/conversations/{id}/messages answers in the conversation. The channel, your number and the person's number come from the conversation, so you only send the text.

curl -X POST "https://api.vlayers.ai/v1/conversations/$CONVERSATION_ID/messages" \
  -H "Authorization: Bearer $VOICELAYER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "content": {
      "type": "text",
      "text": "Yes, we have a table at 7pm. Shall I book it?"
    }
  }'

In test mode the answer looks like this (a live reply starts as queued):

{
  "message": {
    "id": "8d1f6b62-3c8e-4f9a-b7d4-e325f9a2b7d4",
    "conversationId": "7c3e7a51-9b2d-4e8f-a6c3-d21f5b0c7c3e",
    "channel": "sms",
    "direction": "outbound",
    "status": "sent",
    "text": "Yes, we have a table at 7pm. Shall I book it?",
    "parts": [{ "id": "8d1f6b62-3c8e-4f9a-b7d4-e325f9a2b7d4", "status": "sent" }]
  }
}

The full message has more fields: see Reply in a conversation.

The answer is 202 Accepted. A live message starts as queued; in test mode the reply goes to a simulated carrier at once, so you see sent. It never reaches a real phone, never becomes delivered, and costs nothing.

Step 6: See the whole conversation

Run the command from Step 4 again. You now see both messages: the customer's question and your reply, with its status.

Test it: send a message first

You can also message someone first. This texts the reserved number +15550123 directly. If you used that address in Step 3, the message joins the same conversation: a conversation is one of your senders and one person.

curl -X POST https://api.vlayers.ai/v1/messages \
  -H "Authorization: Bearer $VOICELAYER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "channel": "sms",
    "to": "+15550123",
    "content": {
      "type": "text",
      "text": "Your table for two is booked for 7pm."
    }
  }'
Response · 202
{
  "message": {
    "id": "0192f5b0-8d1f-7b62-8c3e-5f9a2b7d4e32",
    "conversationId": "0192f5b0-7c3e-7a51-9b2d-4e8f1a6c3d21",
    "channel": "sms",
    "direction": "outbound",
    "status": "queued",
    "text": "Your table for two is booked for 7pm.",
    "media": [],
    "from": "+14155550100",
    "to": "+15550123",
    "bindingId": null,
    "metadata": {},
    "error": null,
    "createdAt": "2026-10-20T15:04:05.000Z",
    "parts": [
      {
        "id": "0192f5b0-8d1f-7b62-8c3e-5f9a2b7d4e32",
        "status": "queued"
      }
    ]
  }
}

Common errors

ErrorStatusHow to fix
unauthorized401Send Authorization: Bearer <key> with a secret key from the dashboard (Developers → API keys). Test keys start with vl_test_.
sender_ambiguous409Name one of details.senders in from.
test_address_required422Use a reserved test address, or a live key to reach a real person.
test_mode_only403Use a test key (vl_test_…) from the dashboard.
test_limit_reached429Try again after midnight UTC, or ask for a higher test limit.

Next steps

On this page