telenoaHelp
API

Endpoints with examples

Every endpoint in API v1 — inquiries, contacts, calls, knowledge and Noa Voice — with examples in curl and JavaScript.

All addresses start with https://app.telenoa.com/api/v1. The full, machine-readable description with every field is in openapi.json — you can import it into Postman, Insomnia or a code generator.

The JavaScript examples use this small helper (Node 18 or newer):

const TELENOA = "https://app.telenoa.com/api/v1";
const KEY = process.env.TELENOA_API_KEY; // tln_live_…

async function telenoa(path, { method = "GET", body, idempotencyKey } = {}) {
  const res = await fetch(TELENOA + path, {
    method,
    headers: {
      Authorization: `Bearer ${KEY}`,
      ...(body ? { "Content-Type": "application/json" } : {}),
      ...(idempotencyKey ? { "Idempotency-Key": idempotencyKey } : {}),
    },
    body: body ? JSON.stringify(body) : undefined,
  });
  const data = await res.json();
  if (!res.ok) throw new Error(`${data.fejl.kode}: ${data.fejl.besked}`);
  return data;
}

Inquiries (henvendelser)

An inquiry is an email, a form (also from the chat widget) or a call — the same as in Henvendelser (Inquiries) in the app.

Field
statusmodtaget (received), i_gang (in progress), afventer (waiting) or loest (resolved)
mappeindbakke (inbox), arkiv (archive) or spam
kanalmail, formular (form) or opkald (call)
prioritetlav (low), mellem (medium) or hoej (high)
kategoriThe category (your tag on the inquiry), for example { "id": "…", "navn": "Quote" }
tildelt_tilThe team member it's assigned to, or null
resumeNoa's summary
ekstern_idYour own id, if you created it with one

List inquiries

GET /henvendelser — requires henvendelser:read.

Filters: status, mappe (default indbakke), kanal, tildelt_til (a user id or ingen for unassigned), kategori_id, kontakt_id, oprettet_efter (created after), oprettet_foer (created before), opdateret_efter (updated after), q (free text), limit and cursor.

curl "https://app.telenoa.com/api/v1/henvendelser?status=modtaget&kanal=mail&limit=20" \
  -H "Authorization: Bearer $TELENOA_API_KEY"
// Everything changed since the last sync
let cursor = null;
do {
  const page = await telenoa(`/henvendelser?opdateret_efter=${lastSync}&limit=100${cursor ? `&cursor=${cursor}` : ""}`);
  for (const inquiry of page.data) await save(inquiry);
  cursor = page.naeste_cursor;
} while (cursor);

Get one inquiry

GET /henvendelser/{id} — requires henvendelser:read. The response also has beskeder: the email thread, oldest first.

curl https://app.telenoa.com/api/v1/henvendelser/6f1c… \
  -H "Authorization: Bearer $TELENOA_API_KEY"

Create an inquiry

POST /henvendelser — requires henvendelser:write.

The inquiry is handled exactly like a form on your website: rules and routing, spam check, Noa's summary and draft, receipt to the customer and notifications. It belongs to the source API under Opsætning → Kilder (Setup → Sources). If you pause that source, the API can't create inquiries.

Send at least besked (message) or kontakt.email.

curl -X POST https://app.telenoa.com/api/v1/henvendelser \
  -H "Authorization: Bearer $TELENOA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: order-1001" \
  -d '{
    "emne": "Question about order 1001",
    "besked": "When will my order be delivered?",
    "kontakt": { "navn": "Anna Jensen", "email": "anna@company.com" },
    "ekstern_id": "ORD-1001",
    "felter": { "Order number": "1001" }
  }'
const created = await telenoa("/henvendelser", {
  method: "POST",
  idempotencyKey: `order-${order.id}`,
  body: {
    emne: `Question about order ${order.id}`,
    besked: order.comment,
    kontakt: { navn: order.name, email: order.email },
    ekstern_id: String(order.id),
  },
});

felter are extra details like in a form (at most 20). Noa sees them when she writes her summary and draft.

Update an inquiry

PATCH /henvendelser/{id} — requires henvendelser:write. Send only the fields to change: status, mappe, prioritet, tildelt_til, kategori_id, stjernemarkeret (starred), vigtig (important) and svarfrist (reply-by date, YYYY-MM-DD). null removes the assignee, category and reply-by date.

curl -X PATCH https://app.telenoa.com/api/v1/henvendelser/6f1c… \
  -H "Authorization: Bearer $TELENOA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "status": "loest" }'

The change shows on the inquiry's timeline in the app with the key's name. When an inquiry is resolved, the satisfaction survey is sent if you've turned it on.

Reply to the customer

POST /henvendelser/{id}/svar — requires henvendelser:write.

The reply is sent right away from your email account — the one the customer wrote to, otherwise the first active one. It's the same sending as Send on Noa's draft in the app, and the reply appears in the thread afterwards. Blank lines in tekst become paragraphs.

curl -X POST https://app.telenoa.com/api/v1/henvendelser/6f1c…/svar \
  -H "Authorization: Bearer $TELENOA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: reply-6f1c-1" \
  -d '{ "tekst": "Hi Anna\n\nYour order will be delivered on Thursday.\n\nBest regards\nThe shop" }'

The answer is 409 with the code konflikt if the inquiry has no email address, or if you have no active email account under Forbindelser → Mail.

Always use an Idempotency-Key when sending replies. Then the customer won't get the same email twice if your system retries.

Contacts (kontakter)

A contact is a customer in your customer list, with status: ny (new), i_gang (in progress), tilbud_sendt (quote sent), vundet (won) or tabt (lost).

List and find contacts

GET /kontakter — requires kontakter:read. Filters: status, q, email, telefon (phone), opdateret_efter, limit and cursor.

curl "https://app.telenoa.com/api/v1/kontakter?email=anna@company.com" \
  -H "Authorization: Bearer $TELENOA_API_KEY"

GET /kontakter/{id} gets one contact.

Create a contact

POST /kontakter — requires kontakter:write. A contact needs email or telefon. If a contact with the same email or phone already exists, you get that one instead (200 and "oprettet": false) — so no duplicates are made.

const { kontakt, oprettet } = await telenoa("/kontakter", {
  method: "POST",
  body: { navn: "Anna Jensen", email: "anna@company.com", firma: "Company Ltd", status: "tilbud_sendt" },
});

Update a contact

PATCH /kontakter/{id} — requires kontakter:write. Fields: navn (name), firma (company), cvr, email, telefon, adresse (address) and status.

curl -X PATCH https://app.telenoa.com/api/v1/kontakter/91ab… \
  -H "Authorization: Bearer $TELENOA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "status": "vundet" }'

Calls (opkald)

Calls Noa has taken on Noa Voice or in «Talk to Noa».

GET /opkald — requires opkald:read. Filters: udfald (outcome: besked message, booket booked, omstillet transferred, ubesvaret unanswered), startet_efter, startet_foer, limit and cursor.

GET /opkald/{id} also gives udskrift (transcript): what the caller and Noa said, and what Noa did along the way.

curl "https://app.telenoa.com/api/v1/opkald?udfald=besked" \
  -H "Authorization: Bearer $TELENOA_API_KEY"
const call = await telenoa(`/opkald/${id}`);
console.log(call.resultat.titel, call.resume);
for (const turn of call.udskrift) console.log(turn.hvem, turn.tekst);

A call has the same id as the inquiry it's stored as. Recordings are not part of the API.

Knowledge for Noa (viden)

POST /viden/noter — requires viden:write. The note goes into the knowledge base, and Noa uses it in emails, chat and on the phone. titel (title) at most 120 characters, indhold (content) at most 20,000.

curl -X POST https://app.telenoa.com/api/v1/viden/noter \
  -H "Authorization: Bearer $TELENOA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "titel": "Delivery in December", "indhold": "Orders after 20 December are delivered on 2 January." }'

If the knowledge base is full, Telenoa answers 409.

Noa Voice

GET /noa/linje — requires noa:read. Status, number and this month's numbers.

{
  "har_linje": true,
  "status": "aktiv",
  "nummer": "+45 35 15 37 20",
  "nummer_e164": "+4535153720",
  "viderestillet_fra": "+45 70 20 30 40",
  "aktiveret": "2026-09-01T08:00:00.000Z",
  "denne_maaned": { "opkald": 112, "minutter": 240, "klaret_af_noa": 71, "beskeder": 30, "omstillet": 11 }
}

See also

On this page