Numbers, calls and SMS
A phone number belongs to a profile. Every number takes and makes calls; SMS is turned on for the numbers that can send it. Owners and admins (and API keys) buy, set up and release numbers; any member can call and send SMS.
Everything comes out of your balance: each number monthly (€3 to €30, depending on the country and type), calls per minute and SMS per segment.
Choose and buy a number
Section titled “Choose and buy a number”Countries, their number types and the monthly price (in cents):
curl "https://api.marsial.dev/v1/phone-numbers/countries" \ -H "Authorization: Bearer $MARSIAL_API_KEY"const res = await fetch('https://api.marsial.dev/v1/phone-numbers/countries', { headers: { Authorization: `Bearer ${process.env.MARSIAL_API_KEY}`, },});const { data } = await res.json();import os, requests
res = requests.get( "https://api.marsial.dev/v1/phone-numbers/countries", headers={"Authorization": f"Bearer {os.environ['MARSIAL_API_KEY']}"},)data = res.json()["data"]In countries that sell instantly (such as the US or the UK) you can search for an exact number and buy it:
curl "https://api.marsial.dev/v1/phone-numbers/available?country=GB&numberType=mobile&sms=true" \ -H "Authorization: Bearer $MARSIAL_API_KEY"const res = await fetch('https://api.marsial.dev/v1/phone-numbers/available?country=GB&numberType=mobile&sms=true', { headers: { Authorization: `Bearer ${process.env.MARSIAL_API_KEY}`, },});const { data } = await res.json();import os, requests
res = requests.get( "https://api.marsial.dev/v1/phone-numbers/available?country=GB&numberType=mobile&sms=true", headers={"Authorization": f"Bearer {os.environ['MARSIAL_API_KEY']}"},)data = res.json()["data"]curl -X POST "https://api.marsial.dev/v1/phone-numbers" \ -H "Authorization: Bearer $MARSIAL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "profileId": "prf_8f2k4d", "country": "GB", "numberType": "mobile", "phoneNumber": "+447700900123", "sms": true }'const res = await fetch('https://api.marsial.dev/v1/phone-numbers', { method: 'POST', headers: { Authorization: `Bearer ${process.env.MARSIAL_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ "profileId": "prf_8f2k4d", "country": "GB", "numberType": "mobile", "phoneNumber": "+447700900123", "sms": true }),});const { data } = await res.json();import os, requests
res = requests.post( "https://api.marsial.dev/v1/phone-numbers", headers={"Authorization": f"Bearer {os.environ['MARSIAL_API_KEY']}"}, json={ "profileId": "prf_8f2k4d", "country": "GB", "numberType": "mobile", "phoneNumber": "+447700900123", "sms": True },)data = res.json()["data"]Without phoneNumber, you get any number in the country (or in the areaCode). The balance must cover at least one month of the number; otherwise the answer is 402. Send idempotencyKey if you may retry the request: a retry never buys a second number.
Countries with KYC (Spain and most of Europe)
Section titled “Countries with KYC (Spain and most of Europe)”Where the law requires identifying the holder, buying answers 202 with status: "kyc_required". Get the country’s form:
curl "https://api.marsial.dev/v1/phone-numbers/kyc?country=ES" \ -H "Authorization: Bearer $MARSIAL_API_KEY"const res = await fetch('https://api.marsial.dev/v1/phone-numbers/kyc?country=ES', { headers: { Authorization: `Bearer ${process.env.MARSIAL_API_KEY}`, },});const { data } = await res.json();import os, requests
res = requests.get( "https://api.marsial.dev/v1/phone-numbers/kyc?country=ES", headers={"Authorization": f"Bearer {os.environ['MARSIAL_API_KEY']}"},)data = res.json()["data"]Each field has a kind:
textanddate: go invalues, byrequirementId(dates asYYYY-MM-DD).file: upload it first (PDF, JPEG or PNG, up to 4 MB, in base64) and use thedocumentIdit returns. For an ID card, both sides in one file.address: goes inaddress.action: an identity check the holder completes later through a link. Send their legal name inendUserFirstNameandendUserLastName.
curl -X POST "https://api.marsial.dev/v1/phone-numbers/kyc/documents" \ -H "Authorization: Bearer $MARSIAL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "filename": "id.pdf", "contentType": "application/pdf", "data": "JVBERi0xLjQK…" }'const res = await fetch('https://api.marsial.dev/v1/phone-numbers/kyc/documents', { method: 'POST', headers: { Authorization: `Bearer ${process.env.MARSIAL_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ "filename": "id.pdf", "contentType": "application/pdf", "data": "JVBERi0xLjQK…" }),});const { data } = await res.json();import os, requests
res = requests.post( "https://api.marsial.dev/v1/phone-numbers/kyc/documents", headers={"Authorization": f"Bearer {os.environ['MARSIAL_API_KEY']}"}, json={ "filename": "id.pdf", "contentType": "application/pdf", "data": "JVBERi0xLjQK…" },)data = res.json()["data"]curl -X POST "https://api.marsial.dev/v1/phone-numbers/kyc" \ -H "Authorization: Bearer $MARSIAL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "profileId": "prf_8f2k4d", "country": "ES", "values": { "d7e8f9a0-…": "Acme SL" }, "documents": [ { "requirementId": "e8f9a0b1-…", "documentId": "kdoc_…" } ], "address": { "requirementId": "f9a0b1c2-…", "countryCode": "ES", "streetAddress": "Gran Vía 1", "locality": "Madrid", "postalCode": "28013" }, "submissionId": "order-123" }'const res = await fetch('https://api.marsial.dev/v1/phone-numbers/kyc', { method: 'POST', headers: { Authorization: `Bearer ${process.env.MARSIAL_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ "profileId": "prf_8f2k4d", "country": "ES", "values": { "d7e8f9a0-…": "Acme SL" }, "documents": [ { "requirementId": "e8f9a0b1-…", "documentId": "kdoc_…" } ], "address": { "requirementId": "f9a0b1c2-…", "countryCode": "ES", "streetAddress": "Gran Vía 1", "locality": "Madrid", "postalCode": "28013" }, "submissionId": "order-123" }),});const { data } = await res.json();import os, requests
res = requests.post( "https://api.marsial.dev/v1/phone-numbers/kyc", headers={"Authorization": f"Bearer {os.environ['MARSIAL_API_KEY']}"}, json={ "profileId": "prf_8f2k4d", "country": "ES", "values": { "d7e8f9a0-…": "Acme SL" }, "documents": [ { "requirementId": "e8f9a0b1-…", "documentId": "kdoc_…" } ], "address": { "requirementId": "f9a0b1c2-…", "countryCode": "ES", "streetAddress": "Gran Vía 1", "locality": "Madrid", "postalCode": "28013" }, "submissionId": "order-123" },)data = res.json()["data"]The number stays pending_regulatory while the carrier reviews the details (1 to 3 business days) and is not charged until it is active. If the carrier asks for the identity check, the number has regulatory.idVerificationUrl: send it to the holder. If it declines the details, the number becomes regulatory_declined with the reason in regulatory.declineReason.
Inbound calls
Section titled “Inbound calls”Decide where calls go and what happens around them. Only the fields you send change:
curl -X PATCH "https://api.marsial.dev/v1/phone-numbers/num_…/calls" \ -H "Authorization: Bearer $MARSIAL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "forwardTo": "tel:+447700900456", "voicemailEnabled": true, "voicemailGreeting": "Hi, you have reached Acme. Please leave a message.", "recordingEnabled": true, "transcriptionEnabled": true, "businessHoursEnabled": true, "businessHoursTimezone": "Europe/London", "businessHours": [ { "day": 1, "open": "09:00", "close": "18:00" } ] }'const res = await fetch('https://api.marsial.dev/v1/phone-numbers/num_…/calls', { method: 'PATCH', headers: { Authorization: `Bearer ${process.env.MARSIAL_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ "forwardTo": "tel:+447700900456", "voicemailEnabled": true, "voicemailGreeting": "Hi, you have reached Acme. Please leave a message.", "recordingEnabled": true, "transcriptionEnabled": true, "businessHoursEnabled": true, "businessHoursTimezone": "Europe/London", "businessHours": [ { "day": 1, "open": "09:00", "close": "18:00" } ] }),});const { data } = await res.json();import os, requests
res = requests.patch( "https://api.marsial.dev/v1/phone-numbers/num_…/calls", headers={"Authorization": f"Bearer {os.environ['MARSIAL_API_KEY']}"}, json={ "forwardTo": "tel:+447700900456", "voicemailEnabled": True, "voicemailGreeting": "Hi, you have reached Acme. Please leave a message.", "recordingEnabled": True, "transcriptionEnabled": True, "businessHoursEnabled": True, "businessHoursTimezone": "Europe/London", "businessHours": [ { "day": 1, "open": "09:00", "close": "18:00" } ] },)data = res.json()["data"]forwardTo can be a phone (tel:+E164), a SIP address (sip:…) or an AI voice agent over WebSocket (wss://…). Without forwarding, or out of hours, the call goes to voicemail. Recording plays a notice first; recording and transcription cost a little more per minute.
Call someone
Section titled “Call someone”Marsial calls to from your number and, when they answer, connects them with forwardTo: your mobile, a SIP address or your voice agent.
curl -X POST "https://api.marsial.dev/v1/calls" \ -H "Authorization: Bearer $MARSIAL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "numberId": "num_…", "to": "+447700900123", "forwardTo": "tel:+447700900456", "record": true }'const res = await fetch('https://api.marsial.dev/v1/calls', { method: 'POST', headers: { Authorization: `Bearer ${process.env.MARSIAL_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ "numberId": "num_…", "to": "+447700900123", "forwardTo": "tel:+447700900456", "record": true }),});const { data } = await res.json();import os, requests
res = requests.post( "https://api.marsial.dev/v1/calls", headers={"Authorization": f"Bearer {os.environ['MARSIAL_API_KEY']}"}, json={ "numberId": "num_…", "to": "+447700900123", "forwardTo": "tel:+447700900456", "record": True },)data = res.json()["data"]You can check the cost first:
curl "https://api.marsial.dev/v1/calls/estimate?to=%2B447700900123&minutes=5&numberId=num_%E2%80%A6" \ -H "Authorization: Bearer $MARSIAL_API_KEY"const res = await fetch('https://api.marsial.dev/v1/calls/estimate?to=%2B447700900123&minutes=5&numberId=num_%E2%80%A6', { headers: { Authorization: `Bearer ${process.env.MARSIAL_API_KEY}`, },});const { data } = await res.json();import os, requests
res = requests.get( "https://api.marsial.dev/v1/calls/estimate?to=%2B447700900123&minutes=5&numberId=num_%E2%80%A6", headers={"Authorization": f"Bearer {os.environ['MARSIAL_API_KEY']}"},)data = res.json()["data"]With numberId, the answer also says whether that number can reach the destination (reachable). Some numbers, Canada’s for one, only call and text within their own country: then reachable is false and note says why.
A number’s history, with duration, cost and a snippet of the transcript:
curl "https://api.marsial.dev/v1/phone-numbers/num_…/calls" \ -H "Authorization: Bearer $MARSIAL_API_KEY"const res = await fetch('https://api.marsial.dev/v1/phone-numbers/num_…/calls', { headers: { Authorization: `Bearer ${process.env.MARSIAL_API_KEY}`, },});const { data } = await res.json();import os, requests
res = requests.get( "https://api.marsial.dev/v1/phone-numbers/num_…/calls", headers={"Authorization": f"Bearer {os.environ['MARSIAL_API_KEY']}"},)data = res.json()["data"]GET /v1/calls/{id} returns the full transcript and GET /v1/calls/{id}/recording a link to the recording that lasts 15 minutes.
Turn SMS on for a number that can send it:
curl -X PUT "https://api.marsial.dev/v1/phone-numbers/num_…/sms" \ -H "Authorization: Bearer $MARSIAL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "enabled": true }'const res = await fetch('https://api.marsial.dev/v1/phone-numbers/num_…/sms', { method: 'PUT', headers: { Authorization: `Bearer ${process.env.MARSIAL_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ "enabled": true }),});const { data } = await res.json();import os, requests
res = requests.put( "https://api.marsial.dev/v1/phone-numbers/num_…/sms", headers={"Authorization": f"Bearer {os.environ['MARSIAL_API_KEY']}"}, json={ "enabled": True },)data = res.json()["data"]curl -X POST "https://api.marsial.dev/v1/sms/messages" \ -H "Authorization: Bearer $MARSIAL_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "numberId": "num_…", "to": "+447700900123", "text": "Your order 4821 is on its way" }'const res = await fetch('https://api.marsial.dev/v1/sms/messages', { method: 'POST', headers: { Authorization: `Bearer ${process.env.MARSIAL_API_KEY}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ "numberId": "num_…", "to": "+447700900123", "text": "Your order 4821 is on its way" }),});const { data } = await res.json();import os, requests
res = requests.post( "https://api.marsial.dev/v1/sms/messages", headers={"Authorization": f"Bearer {os.environ['MARSIAL_API_KEY']}"}, json={ "numberId": "num_…", "to": "+447700900123", "text": "Your order 4821 is on its way" },)data = res.json()["data"]Replies arrive in the inbox as conversations of platform sms, and at your webhook as message.received. Whoever replies STOP cannot get SMS from that number again (GET /v1/phone-numbers/{id}/sms/opt-outs).
In the dashboard and for agents
Section titled “In the dashboard and for agents”In the dashboard, the Phone section has the numbers (buy, the holder’s form, call settings, SMS, release) and the call history with recordings and transcripts. AI agents can list numbers, send SMS, call and read the history through the MCP server; buying numbers is left to people.
We do not offer bringing your number from another carrier (porting), branded senders or calling from the browser yet.
© 2026 Marsial by TWOFACES ·marsial.dev ·Dashboard ·soporte@twofacesco.com