NUACOM API

Getting started

Making requests

Every NUACOM API call is an HTTPS request to one host, authenticated by one header, with JSON in and JSON out. This page is the contract every endpoint shares: the base URL, the headers, how bodies and responses are shaped, the pagination and filtering conventions you will meet across the reference, and what is safe to retry. The token itself is covered in Authentication; Errors, Rate limits and Dates and timezones complete the contract.

Base URL

Send every request to https://api.api-nuacom.com, over HTTPS only. Paths start with /v2/… or /v3/…: both belong to the same API, the same token works on both and one integration mixes them freely, see V2, current. The API finds your account from the token, so there is nothing to add to the host: do not put an account name in front of it, and do not call the host of the NUACOM portal.

Authentication

Send your API key, or a session token from POST /v2/auth/login, as Authorization: Bearer <token> on every call. A missing, invalid or expired token answers 401 with the body "Unauthorized". How to create a key, and when a session token is the better choice, is in the Authentication guide.

Headers

Header Value Notes
Authorization Bearer followed by your API key or session token Required on every endpoint except login
Content-Type application/json For JSON bodies; multipart/form-data for uploads
Accept application/json Responses are JSON either way, errors included
X-Timezone an IANA name such as Europe/Dublin Optional. The timezone your date filters are read in and, on accounts with ISO dates, the offset of returned timestamps. See Dates and timezones

Three things happen to every request before validation: leading and trailing whitespace is trimmed from string values, an empty string becomes null, and on POST, PUT and PATCH every string field except passwords has HTML tags stripped. A <b> inside a note or a contact name does not come back.

Bodies

Send JSON for everything that is not a file. Uploads (a contacts file, MMS or WhatsApp media) are multipart/form-data, with the file in the field the reference names and the other inputs as ordinary form fields.

# JSON body: create a contact
curl -X POST https://api.api-nuacom.com/v3/contacts \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -d '{"first_name": "Jane", "last_name": "Doe", "phones": ["+353861234567"]}'

# Multipart body: import a contacts file
curl -X POST https://api.api-nuacom.com/v3/contacts/import \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -F 'contacts_list=@contacts.csv' \
  -F 'first_name_index=0' -F 'last_name_index=1' -F 'phone_indexes[]=2'

The two endpoints are POST /v3/contacts and POST /v3/contacts/import; the Contacts guide explains both.

Reading responses

  • Every response is JSON. Success is 200, 201 for a created resource, and 204 for a request that only queues work, such as the call-log and contact exports, which arrive by email.
  • Read objects by key and ignore keys you do not know: new optional fields appear without notice, existing ones are not renamed within V2.
  • Ids are typed as the reference shows for each endpoint. Most are integers (a contact, a template, a message status); account ids are strings such as IE-00123; conversation ids are UUIDs; a few legacy endpoints return numbers as strings, the login response's user_id among them.
  • Booleans are true/false on the /v3/… and newer /v2/… endpoints. Legacy endpoints may return "1"/"0" strings or 1/0 integers instead; GET /v2/users_list returns user_active as a string and active_in_account as a boolean in the same row. Trust the example on each field in the reference.
  • Dates are Y-m-d H:i:s wall-clock strings by default, and ISO 8601 with an offset on accounts where NUACOM has switched on ISO dates; see Dates and timezones.

Pagination

Four pagination shapes exist, each tied to a family of endpoints. The reference shows which one an endpoint uses in its response schema.

Call logs and reports. GET /v2/call-log and the reports such as GET /v2/missed-call-report take page and per_page (default 25, no maximum) and answer:

{
  "_embedded": [ { "call_unique_id": "…", "call_date": "2026-05-01 14:30:00", "…": "…" } ],
  "_links": { "…": "…" },
  "page_count": 1,
  "page_size": 25,
  "total_items": 1200
}

page_count is the current page, not the number of pages: paginate on total_items and page_size. _links holds the page URLs, repeated in a Link response header.

Conversations. GET /v2/conversations takes page and per_page (default 15) and answers a page object with the rows in data:

{
  "current_page": 2,
  "data": [ { "id": "3f9c…", "channel_type": 1, "…": "…" } ],
  "from": 16,
  "to": 30,
  "per_page": 15,
  "last_page": 80,
  "total": 1193,
  "next_page_url": "…?page=3",
  "prev_page_url": "…?page=1"
}

first_page_url, last_page_url, path and links come with it. Three filters, expires_in_hours, unread and unassigned, ignore per_page and return up to 500 rows in one page.

/v3/… lists. GET /v3/contacts, GET /v3/number-blocking, GET /v3/call-tags and the other /v3/… collections take page and perPage, both required on most of them. The bounds differ per endpoint: perPage is 1–500 on /v3/contacts and one of 15, 25, 50, 100 on /v3/number-blocking, and GET /v3/users spells it per_page (default 25). Where the reference lists them, sort takes comma-separated fields with - for descending, filter[field]=value filters, and include names optional relations to embed. The answer is a resource collection:

{
  "data": [ { "id": 12345, "first_name": "Jane", "…": "…" } ],
  "links": { "first": "…?page=1", "last": "…?page=40", "prev": null, "next": "…?page=2" },
  "meta": { "current_page": 1, "from": 1, "to": 15, "per_page": 15, "last_page": 40, "total": 600, "path": "…", "links": [ "…" ] }
}

An unknown filter[…] or sort field answers 400 with the allowed names in message.

DataTables offsets. The reports behind the portal's SMS pages page by offset: iDisplayStart (offset of the first row, default 0) and iDisplayLength (rows per page). POST /v2/sms/history (default length 25) echoes your sEcho and answers {"sEcho": 1, "iTotalRecords": 500, "iTotalDisplayRecords": 42, "aaData": […]}, where iTotalDisplayRecords is the count after your filters. GET /v2/sms/inbox (default length 10) and GET /v2/sms/timeline (default 100) answer {"_embedded": […], "total_items": 42}. GET /v2/sms uses page and pageSize. New integrations should read messages through the conversation endpoints instead; see Conversations.

Filtering and sorting

  • /v3/… lists filter with filter[field]=value. Several values are comma-separated and combined with OR (filter[group]=5,7); on /v3/contacts a text term prefixed with ~ is a partial match and an unprefixed one an exact match (filter[last_name]=~doe). Sort with sort=field, or sort=-field for descending, several fields comma-separated.
  • Legacy /v2/… lists read their filters as plain query parameters, comma-separated where several values are allowed (extensions=101,102 on the call log, not extensions[]=101). Some use filter[…] as well, such as GET /v2/sms-templates with filter[channel], filter[status] and filter[search].
  • Date filters are wall-clock strings read in the timezone of X-Timezone, or your user's profile timezone without it; see Dates and timezones.
  • Each group guide lists the filters of its endpoints with their defaults. The generated reference does not always list filters that an endpoint reads from the raw query string.

Retrying requests

There are no idempotency keys. What is safe to repeat follows the HTTP method:

  • GET is always safe to retry.
  • PUT and DELETE are repeatable: a second identical PUT /v3/contacts/{contactId} leaves the contact exactly as the first one did.
  • POST is not. A repeated create makes a second contact (there is no duplicate detection) and a repeated send sends the SMS again. Retry a POST only when you know the first attempt never reached the API (a connection error before any response), or after checking: list the resource you tried to create, or search GET /v2/conversations for the message.

When the API answers 429, wait the number of seconds in Retry-After and repeat the same request; see Rate limits. A 5xx is worth one or two retries with increasing delays; any other 4xx fails the same way until you change the request, see Errors.