Text-to-Speech

Turn text into a recording with a stock or cloned voice.

This block is the Text-to-speech API. The full product reference is the Text-to-speech API reference. This page is the short version: when to use it, one working request, and its limits. There is no widget to embed.

When to use this vs REST

This block is the REST API. Call it from your server with your API key, or from a page with a site token that has the voice scope. There is no browser widget.

Do not rebuild

  • Voice hosting or a voice picker. List voices with GET /voice/public/voices.
  • Audio storage. The response carries a playback URL with an expiry.

Drop-in

POST text and a voice_id. List voices with GET /voice/public/voices. There is no browser widget for this block; a page can call the same route with a site token that has the voice scope.

curl -X POST https://api-v2.dropcowboy.com/voice/public/tts/synthesize \
  -H "x-key: $KEY" -H "x-secret: $SECRET" \
  -H "Content-Type: application/json" \
  -d '{"text":"Hello from Acme","voice_id":"e2b7c9d4-3f1a-4c8e-9b6d-7a5f0e1c2d3b"}'

From Node on your server:

async function synthesizeGreeting() {
  const audio = await fetch('https://api-v2.dropcowboy.com/voice/public/tts/synthesize', {
    method: 'POST',
    headers: { 'x-key': process.env.DC_KEY, 'x-secret': process.env.DC_SECRET, 'Content-Type': 'application/json' },
    body: JSON.stringify({ text: 'Hi, this is Alex from Acme. Leave a message.', voice_id: 'e2b7c9d4-3f1a-4c8e-9b6d-7a5f0e1c2d3b' })
  }).then(function (r) { return r.json(); });
  return audio.data.audio_url;
}

With Twilio

Synthesize on your server, then hand Twilio the audio URL. No widget and no site token in the call flow.

const VoiceResponse = require('twilio').twiml.VoiceResponse;

app.post('/twilio/voice', async function (req, res) {
  const audio = await fetch('https://api-v2.dropcowboy.com/voice/public/tts/synthesize', {
    method: 'POST',
    headers: { 'x-key': process.env.DC_KEY, 'x-secret': process.env.DC_SECRET, 'Content-Type': 'application/json' },
    body: JSON.stringify({ text: 'Hi, this is Alex from Acme. Leave a message.', voice_id: 'e2b7c9d4-3f1a-4c8e-9b6d-7a5f0e1c2d3b' })
  }).then(function (r) { return r.json(); });
  const twiml = new VoiceResponse();
  twiml.play(audio.data.audio_url);
  res.type('text/xml').send(twiml.toString());
});

From a browser

Mint a site token with the voice scope on your server, then call the same route with Authorization: Bearer <token>.

# On your server: trade your API key (needs numbers:write) for a site token. 1 hour max.
curl -s -X POST https://api-v2.dropcowboy.com/phone/public/embed/token \
  -H "x-key: $KEY" -H "x-secret: $SECRET" \
  -H "Content-Type: application/json" \
  -d '{"site_id":"YOUR_SITE_ID","scope":["voice"],"ttl_seconds":900}'

JS API / HTML tag

No browser widget and no HTML tag. Routes:

Route Body What it does
POST /voice/public/tts/synthesize { text, voice_id, language } Returns audio metadata with a playback URL. Scope voice:send.
GET /voice/public/voices ?include_pro_voices=true Stock and team voices you can pass as voice_id. Scope media:read.

Auth and scopes

API key and secret on your server (voice:send). From a browser, send Authorization: Bearer with a site token minted with the voice scope.

Site token scope: voice. Mint it with POST https://api-v2.dropcowboy.com/phone/public/embed/token using an API key with numbers:write. Tokens last up to one hour.

Limits

  • Each call checks prepaid balance and returns 402 when funds are short.
  • A voice must be ready before it can speak; a voice still in checkout returns 402.
  • The audio_url expires at expires_at. Copy the file if you need it longer.

Routes are on https://api-v2.dropcowboy.com.

Copy the code

The requests on this page. KEY and SECRET (DC_KEY and DC_SECRET in Node) are your API key pair; keep them on your server.

Call the API (curl)
curl -X POST https://api-v2.dropcowboy.com/voice/public/tts/synthesize \
  -H "x-key: $KEY" -H "x-secret: $SECRET" \
  -H "Content-Type: application/json" \
  -d '{"text":"Hello from Acme","voice_id":"e2b7c9d4-3f1a-4c8e-9b6d-7a5f0e1c2d3b"}'
Call it from Node
async function synthesizeGreeting() {
  const audio = await fetch('https://api-v2.dropcowboy.com/voice/public/tts/synthesize', {
    method: 'POST',
    headers: { 'x-key': process.env.DC_KEY, 'x-secret': process.env.DC_SECRET, 'Content-Type': 'application/json' },
    body: JSON.stringify({ text: 'Hi, this is Alex from Acme. Leave a message.', voice_id: 'e2b7c9d4-3f1a-4c8e-9b6d-7a5f0e1c2d3b' })
  }).then(function (r) { return r.json(); });
  return audio.data.audio_url;
}
With Twilio (Node)
const VoiceResponse = require('twilio').twiml.VoiceResponse;

app.post('/twilio/voice', async function (req, res) {
  const audio = await fetch('https://api-v2.dropcowboy.com/voice/public/tts/synthesize', {
    method: 'POST',
    headers: { 'x-key': process.env.DC_KEY, 'x-secret': process.env.DC_SECRET, 'Content-Type': 'application/json' },
    body: JSON.stringify({ text: 'Hi, this is Alex from Acme. Leave a message.', voice_id: 'e2b7c9d4-3f1a-4c8e-9b6d-7a5f0e1c2d3b' })
  }).then(function (r) { return r.json(); });
  const twiml = new VoiceResponse();
  twiml.play(audio.data.audio_url);
  res.type('text/xml').send(twiml.toString());
});
Mint a browser token (curl)
# On your server: trade your API key (needs numbers:write) for a site token. 1 hour max.
curl -s -X POST https://api-v2.dropcowboy.com/phone/public/embed/token \
  -H "x-key: $KEY" -H "x-secret: $SECRET" \
  -H "Content-Type: application/json" \
  -d '{"site_id":"YOUR_SITE_ID","scope":["voice"],"ttl_seconds":900}'