Getting started
Quickstart
Six requests take you from a new API key to a webhook and an SMS, in about ten minutes. Every call goes to https://api.api-nuacom.com with the key in an Authorization: Bearer header and Accept: application/json; the Authentication guide covers the key in depth and the alternative, a session token for one user.
The samples use curl, JavaScript (fetch, Node 18 or newer), PHP (the curl extension, no framework) and Python (requests). The three scripts read the key from an environment variable, NUACOM_API_KEY.
1. Create an API key
- Sign in to the NUACOM portal as an account admin and open Settings → Apps and Integrations → API Keys.
- Add a key and name it after the integration you are building; every call made with it is attributed to that name.
- Copy the key when it is shown. The portal shows it once; afterwards the list carries only a masked hint. A lost key cannot be recovered, only replaced and then disabled.
Important
The key acts as the admin who created it, is scoped to that account and never expires. Keep it in a secret store on your server, never in a browser, a mobile app, a log or a repository.
2. First call: the account
GET /v2/account_info returns the account the key belongs to, which makes it the quickest check that the key works.
curl https://api.api-nuacom.com/v2/account_info \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Accept: application/json'
const headers = { Authorization: `Bearer ${process.env.NUACOM_API_KEY}`, Accept: 'application/json' };
const response = await fetch('https://api.api-nuacom.com/v2/account_info', { headers });
const account = await response.json();
console.log(account.account_id, account.company_name);
<?php
$ch = curl_init('https://api.api-nuacom.com/v2/account_info');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('NUACOM_API_KEY'),
'Accept: application/json',
],
]);
$account = json_decode(curl_exec($ch), true);
curl_close($ch);
echo $account['account_id'], ' ', $account['company_name'], PHP_EOL;
import os
import requests
headers = {"Authorization": f"Bearer {os.environ['NUACOM_API_KEY']}", "Accept": "application/json"}
account = requests.get("https://api.api-nuacom.com/v2/account_info", headers=headers).json()
print(account["account_id"], account["company_name"])
200, trimmed:
{
"account_id": "IE-00123",
"company_name": "Acme Ltd",
"company_email": "office@acme.example",
"billing_email": "accounts@acme.example",
"address": {"address_1": "1 Main Street", "city": "Dublin", "country_iso_code": "IE", "post_code": "D02 XY12", …},
"account_holder": {"user_id": 123, "user_email": "jane@acme.example", "user_name": "Jane Doe", …},
"account_country_code": "IE",
"time_zone": "Europe/Dublin",
"billing": {"billing_currency": "EUR", "account_balance": 120.5, "sms_balance": 480, …}
}
401 with the body "Unauthorized" means the key is missing, wrong or disabled.
3. List extensions
GET /v2/extensions answers a plain array with one entry per extension the key's user can see (an admin sees the whole account): number, name, extension_is_used (1 when a user is linked to it) and subscription (the tier name, or null). Creating and configuring extensions is covered in the Extensions guide.
curl https://api.api-nuacom.com/v2/extensions \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Accept: application/json'
const headers = { Authorization: `Bearer ${process.env.NUACOM_API_KEY}`, Accept: 'application/json' };
const response = await fetch('https://api.api-nuacom.com/v2/extensions', { headers });
const extensions = await response.json();
for (const ext of extensions) {
console.log(ext.number, ext.name, ext.extension_is_used ? 'in use' : 'free');
}
<?php
$ch = curl_init('https://api.api-nuacom.com/v2/extensions');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('NUACOM_API_KEY'),
'Accept: application/json',
],
]);
$extensions = json_decode(curl_exec($ch), true);
curl_close($ch);
foreach ($extensions as $ext) {
echo $ext['number'], ' ', $ext['name'], ' ', $ext['extension_is_used'] ? 'in use' : 'free', PHP_EOL;
}
import os
import requests
headers = {"Authorization": f"Bearer {os.environ['NUACOM_API_KEY']}", "Accept": "application/json"}
extensions = requests.get("https://api.api-nuacom.com/v2/extensions", headers=headers).json()
for ext in extensions:
print(ext["number"], ext["name"], "in use" if ext["extension_is_used"] else "free")
200:
[
{"number": 100, "name": "Reception", "extension_is_used": 1, "subscription": "Standard"},
{"number": 101, "name": "Jane Doe", "extension_is_used": 1, "subscription": "Standard"},
{"number": 102, "name": "Meeting room", "extension_is_used": 0, "subscription": null}
]
4. Yesterday's calls
GET /v2/call-log is the call history. Send from_date and to_date as Y-m-d for whole days, or Y-m-d H:i:s for a time of day; send both, because with only one the last month is returned. The dates are read in the timezone of the X-Timezone header (any IANA name), or in the user's profile timezone without it, and call_date in the rows is written the same way. Pages are page and per_page (25 by default).
curl 'https://api.api-nuacom.com/v2/call-log?from_date=2026-10-27&to_date=2026-10-27&per_page=50' \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Accept: application/json' \
-H 'X-Timezone: Europe/Dublin'
const headers = {
Authorization: `Bearer ${process.env.NUACOM_API_KEY}`,
Accept: 'application/json',
'X-Timezone': 'Europe/Dublin',
};
const query = new URLSearchParams({ from_date: '2026-10-27', to_date: '2026-10-27', per_page: '50' });
const response = await fetch(`https://api.api-nuacom.com/v2/call-log?${query}`, { headers });
const page = await response.json();
console.log(`${page.total_items} calls yesterday, ${page._embedded.length} on this page`);
for (const call of page._embedded) {
console.log(call.call_date, call.direction, call.from, '->', call.to, `${call.billsec}s`);
}
<?php
$query = http_build_query(['from_date' => '2026-10-27', 'to_date' => '2026-10-27', 'per_page' => 50]);
$ch = curl_init('https://api.api-nuacom.com/v2/call-log?' . $query);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('NUACOM_API_KEY'),
'Accept: application/json',
'X-Timezone: Europe/Dublin',
],
]);
$page = json_decode(curl_exec($ch), true);
curl_close($ch);
echo $page['total_items'], ' calls yesterday', PHP_EOL;
foreach ($page['_embedded'] as $call) {
echo $call['call_date'], ' ', $call['direction'], ' ', $call['from'], ' -> ', $call['to'], ' ', $call['billsec'], 's', PHP_EOL;
}
import os
import requests
headers = {
"Authorization": f"Bearer {os.environ['NUACOM_API_KEY']}",
"Accept": "application/json",
"X-Timezone": "Europe/Dublin",
}
params = {"from_date": "2026-10-27", "to_date": "2026-10-27", "per_page": 50}
page = requests.get("https://api.api-nuacom.com/v2/call-log", headers=headers, params=params).json()
print(page["total_items"], "calls yesterday")
for call in page["_embedded"]:
print(call["call_date"], call["direction"], call["from"], "->", call["to"], f'{call["billsec"]}s')
200, trimmed to one row:
{
"_embedded": [
{
"call_unique_id": 918273645,
"call_id": "1761554472.4521",
"call_date": "2026-10-27 09:41:12",
"direction": "inbound",
"status": "ANSWERED",
"from": "353861234567",
"from_local": "0861234567",
"from_name": "John Baton",
"to": "35312345678",
"to_local": "012345678",
"to_ext": "101",
"to_ext_name": "Jane Doe",
"duration": 95,
"billsec": 82,
"recording": true,
…
}
],
"_links": {…},
"page_count": 1,
"page_size": 50,
"total_items": 37
}
_embeddedis the page of rows.total_itemsis how many rows match the filters in total andpage_sizethe page length;page_countis the current page number, not the number of pages, so keep requestingpage + 1untilpage × page_sizereachestotal_items.call_unique_idis the row's own id;call_idis shared by every leg of the same call (transfers, queue legs).billsecis the connected time,durationincludes ringing. The Call Logs guide explains every field and filter, and the recording and transcript endpoints.
5. Get told about new calls
Rather than polling the call log, register a webhook: POST /v2/webhook-subscriptions with a type and the url NUACOM should call (up to 500 characters). call_event fires for every call on the account; NUACOM then POSTs a JSON payload to your URL each time. The answer is 201 with the subscription's id, or 200 with the existing id when that URL is already subscribed to that type. Answer the delivery with a 2xx quickly and do the work afterwards.
curl -X POST https://api.api-nuacom.com/v2/webhook-subscriptions \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"type": "call_event", "url": "https://example.com/nuacom/webhook"}'
const headers = {
Authorization: `Bearer ${process.env.NUACOM_API_KEY}`,
Accept: 'application/json',
'Content-Type': 'application/json',
};
const response = await fetch('https://api.api-nuacom.com/v2/webhook-subscriptions', {
method: 'POST',
headers,
body: JSON.stringify({ type: 'call_event', url: 'https://example.com/nuacom/webhook' }),
});
const subscription = await response.json();
console.log(response.status, subscription.id);
<?php
$ch = curl_init('https://api.api-nuacom.com/v2/webhook-subscriptions');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode(['type' => 'call_event', 'url' => 'https://example.com/nuacom/webhook']),
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('NUACOM_API_KEY'),
'Accept: application/json',
'Content-Type: application/json',
],
]);
$subscription = json_decode(curl_exec($ch), true);
echo curl_getinfo($ch, CURLINFO_RESPONSE_CODE), ' ', $subscription['id'], PHP_EOL;
curl_close($ch);
import os
import requests
headers = {"Authorization": f"Bearer {os.environ['NUACOM_API_KEY']}", "Accept": "application/json"}
response = requests.post(
"https://api.api-nuacom.com/v2/webhook-subscriptions",
headers=headers,
json={"type": "call_event", "url": "https://example.com/nuacom/webhook"},
)
print(response.status_code, response.json()["id"])
201:
{"id": 42}
The Webhooks guide lists the other event types and what each payload carries. GET /v2/webhook-subscriptions lists your subscriptions and DELETE /v2/webhook-subscriptions/{webhookSubscription} removes one.
6. Send an SMS
POST /v2/sms/send sends one message to one or more numbers. from is an approved sender name of the account (requested once, in the portal or with the API, as the Conversations / SMS guide describes), message is the text, and to[] lists the recipients, each with a number in international format and an optional reference that is echoed back. The account needs SMS credit.
curl -X POST https://api.api-nuacom.com/v2/sms/send \
-H 'Authorization: Bearer YOUR_API_KEY' \
-H 'Accept: application/json' \
-H 'Content-Type: application/json' \
-d '{"from": "ACME", "message": "Your order 1001 is ready for collection.", "to": [{"number": "+353861234567", "reference": "order-1001"}]}'
const headers = {
Authorization: `Bearer ${process.env.NUACOM_API_KEY}`,
Accept: 'application/json',
'Content-Type': 'application/json',
};
const response = await fetch('https://api.api-nuacom.com/v2/sms/send', {
method: 'POST',
headers,
body: JSON.stringify({
from: 'ACME',
message: 'Your order 1001 is ready for collection.',
to: [{ number: '+353861234567', reference: 'order-1001' }],
}),
});
const result = await response.json();
console.log(result.status, result.numbers?.[0]?.message_id);
<?php
$ch = curl_init('https://api.api-nuacom.com/v2/sms/send');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode([
'from' => 'ACME',
'message' => 'Your order 1001 is ready for collection.',
'to' => [['number' => '+353861234567', 'reference' => 'order-1001']],
]),
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('NUACOM_API_KEY'),
'Accept: application/json',
'Content-Type: application/json',
],
]);
$result = json_decode(curl_exec($ch), true);
curl_close($ch);
echo $result['status'], ' ', $result['numbers'][0]['message_id'] ?? '', PHP_EOL;
import os
import requests
headers = {"Authorization": f"Bearer {os.environ['NUACOM_API_KEY']}", "Accept": "application/json"}
result = requests.post(
"https://api.api-nuacom.com/v2/sms/send",
headers=headers,
json={
"from": "ACME",
"message": "Your order 1001 is ready for collection.",
"to": [{"number": "+353861234567", "reference": "order-1001"}],
},
).json()
print(result["status"], result["numbers"][0].get("message_id"))
200, trimmed:
{
"status": "sent",
"success_count": 1,
"failed_count": 0,
"numbers": [
{
"number": "+353861234567",
"international_number": "+353861234567",
"reference": "order-1001",
"valid": true,
"message_id": "01K8Q2ZT6M8K9W7V5R4H3N2P1Q"
}
]
}
status is sent, scheduled, partial or failed; a recipient that failed carries an error instead of a message_id. Keep message_id: it is the key for the delivery-status endpoints in the Conversations guide. A badly formed request answers 400 with {"code": 400, "error": "Not well formatted", "validation_errors": {…}}; a send the platform refuses answers with a code in the body, for example 6001 for insufficient credit or 6003 for a sender name that is not approved, all listed in the SMS guide.
Where next
- Authentication: API keys versus session tokens, lifetimes, and what
401and403mean. - Making requests: the headers, pagination and filter conventions shared by every endpoint.
- Errors: status codes and error bodies across the API.
- API reference: every endpoint with its schemas and a console to try requests from this site.