Invoices
9 operations. Each REST operation is also an MCP tool of the same name. REST https://tlntconnect.com/api/v1
GET/api/v1/invoices
Every invoice of the workspace (admin members on keys with finance:read only; a non-admin member is 403 role_forbidden, an admin key without the scope 403 missing_scope). Rows: id, invoice_number, status (draft, sent, paid, overdue, cancelled), campaign {id, name}, brand {id, name}, amount_cents, tax_cents, total_cents, currency, issued_date, due_date, sent_at, paid_date, voided_at, billing_name, billing_email, payment_status, created_at, updated_at. Filter by status, campaign_id or brand_id; paged with page/limit (limit ≤ 100, default 50). Billing links and provider ids are never returned.
list_invoicesfinance:read- admin
- Ignored (read)
- 1
Input
status query"draft" | "sent" | "paid" | "overdue" | "cancelled"campaign_id querystring (uuid) — Only this campaign's invoices.brand_id querystring (uuid) — Only this brand's invoices.limit queryinteger — Page size, default 50.page queryinteger — 1-based page, default 1.Responses
POST/api/v1/invoices
Creates a DRAFT invoice through the same path as the app (src/lib/invoices/drafts.ts): number INV-YYYYMMDD-NNN, status draft, total_cents = amount_cents + tax_cents. With line_items, amount_cents is their sum (sending amount_cents too is a 400 unless it equals that sum); without, send amount_cents. brand_id / campaign_id / a line's creator_id or deliverable_id from another workspace is 404 / 400 before anything is written. Sending is a separate send_invoice, which needs the user's written confirmation in chat. Requires an Idempotency-Key (a blind retry never creates a second draft). Admin members on keys with finance:read + invoices:write.
create_invoicefinance:read+invoices:write- admin
- required
- 1
Input
Idempotency-Key header, requiredstring — Retry-safe key (1-255 printable ASCII; a UUID is recommended; an RFC 8941 quoted string is accepted). The same key with the same request within 24h replays the stored response byte for byte (Idempotent-Replayed: true) without running again; the replay still consumes the operation's rate weight.brand_id bodystring (uuid) | nullcampaign_id bodystring (uuid) | nullamount_cents bodyinteger — Subtotal in integer cents (omit when sending line_items).tax_cents bodyinteger — Tax in integer cents, default 0.currency bodystring — ISO currency, default USD.issued_date bodystring | nulldue_date bodystring | nullnotes bodystring | nullemail_subject bodystring | nullemail_message bodystring | nullbilling_name bodystring | nullbilling_email bodystring (email) | nullbilling_address_line1 bodystring | nullbilling_address_line2 bodystring | nullbilling_city bodystring | nullbilling_state bodystring | nullbilling_postal_code bodystring | nullbilling_country bodystring | nullline_items bodyarray of objectResponses
GET/api/v1/invoices/{invoice_id}
The list row plus notes, the billing address, the composed email subject/message, payment_contract_file_id, creator_payout_portion_cents (when a direct payment recorded it) and line_items (id, description, quantity, unit_price_cents, total_cents, creator_id, deliverable_id). Another workspace's invoice is 404.
get_invoicefinance:read- admin
- Ignored (read)
- 1
Input
invoice_id path, requiredstring (uuid) — Invoice id in this workspace.Responses
PATCH/api/v1/invoices/{invoice_id}
Drafts only (a sent, paid or cancelled invoice is 409 conflict: change it in TLNT). expected_updated_at is required (428 expected_updated_at_required without it; 409 stale_target when the invoice changed since). The amount is the sum of the line items — edit those with the line-item tools; total_cents is recomputed from it and tax_cents. Status, sending and payment never change here.
update_invoicefinance:read+invoices:write- admin
- optional
- 1
Input
invoice_id path, requiredstring (uuid) — Invoice id in this workspace.Idempotency-Key headerstring — Retry-safe key (1-255 printable ASCII; a UUID is recommended; an RFC 8941 quoted string is accepted). The same key with the same request within 24h replays the stored response byte for byte (Idempotent-Replayed: true) without running again; the replay still consumes the operation's rate weight.expected_updated_at bodystring — The invoice's updated_at as you last read it. A changed invoice is refused (409 stale_target). In a query string, URL-encode it (+ as %2B).tax_cents bodyintegercurrency bodystringissued_date bodystring | nulldue_date bodystring | nullnotes bodystring | nullemail_subject bodystring | nullemail_message bodystring | nullbilling_name bodystring | nullbilling_email bodystring (email) | nullbilling_address_line1 bodystring | nullbilling_address_line2 bodystring | nullbilling_city bodystring | nullbilling_state bodystring | nullbilling_postal_code bodystring | nullbilling_country bodystring | nullResponses
DELETE/api/v1/invoices/{invoice_id}
Deletes one unlocked DRAFT invoice (a sent, paid, cancelled or payment-started invoice is 409 conflict). A `delete`-class action: by default it deletes at once (200 { status: "deleted" }) within the credential's daily delete cap. When the credential asks for approval in TLNT for deletes, the call answers 202 { status: "pending_approval", action_id, approval_url } instead and a workspace admin approves it in TLNT — the card names the invoice number, total and line-item count at the version you asked; an invoice edited before approval is not deleted. Requires finance:read + invoices:write + delete, an Idempotency-Key and an admin member.
delete_invoicefinance:read+invoices:write+delete- admin
- Runs directly; queued when the credential asks for approval for delete
- required
- 1
Input
invoice_id path, requiredstring (uuid) — Invoice id in this workspace.expected_updated_at querystring — The invoice's updated_at as you last read it. A changed invoice is refused (409 stale_target). In a query string, URL-encode it (+ as %2B).Idempotency-Key header, requiredstring — Retry-safe key (1-255 printable ASCII; a UUID is recommended; an RFC 8941 quoted string is accepted). The same key with the same request within 24h replays the stored response byte for byte (Idempotent-Replayed: true) without running again; the replay still consumes the operation's rate weight.Responses
POST/api/v1/invoices/{invoice_id}/line-items
Same path as the app (src/lib/invoices/drafts.ts): integer quantity ≥ 1, unit_price_cents in integer cents, total = quantity × unit price; creator_id / deliverable_id must be this workspace's (400 otherwise). Drafts only over the API (409 conflict otherwise). Requires an Idempotency-Key (a blind retry never adds the line twice). Returns the invoice with its recalculated totals.
add_invoice_line_itemfinance:read+invoices:write- admin
- required
- 1
Input
invoice_id path, requiredstring (uuid) — Invoice id in this workspace.Idempotency-Key header, requiredstring — Retry-safe key (1-255 printable ASCII; a UUID is recommended; an RFC 8941 quoted string is accepted). The same key with the same request within 24h replays the stored response byte for byte (Idempotent-Replayed: true) without running again; the replay still consumes the operation's rate weight.description body, requiredstringquantity bodyinteger — Default 1.unit_price_cents body, requiredinteger — Unit price in integer cents.creator_id bodystring (uuid) | null — A roster creator in this workspace.deliverable_id bodystring (uuid) | null — A deliverable in this workspace.Responses
PATCH/api/v1/invoices/{invoice_id}/line-items/{line_item_id}
As add_invoice_line_item, for one existing line (404 when it is not on this invoice). Drafts only over the API.
update_invoice_line_itemfinance:read+invoices:write- admin
- optional
- 1
Input
invoice_id path, requiredstring (uuid) — Invoice id in this workspace.line_item_id path, requiredstring (uuid) — Line item id on this invoice.Idempotency-Key headerstring — Retry-safe key (1-255 printable ASCII; a UUID is recommended; an RFC 8941 quoted string is accepted). The same key with the same request within 24h replays the stored response byte for byte (Idempotent-Replayed: true) without running again; the replay still consumes the operation's rate weight.description bodystringquantity bodyintegerunit_price_cents bodyintegerResponses
DELETE/api/v1/invoices/{invoice_id}/line-items/{line_item_id}
Removes one line of a DRAFT invoice (an edit of the draft, so it runs directly under invoices:write; 404 when the line is not on this invoice). Returns the invoice with its recalculated totals.
remove_invoice_line_itemfinance:read+invoices:write- admin
- optional
- 1
Input
invoice_id path, requiredstring (uuid) — Invoice id in this workspace.line_item_id path, requiredstring (uuid) — Line item id on this invoice.Idempotency-Key headerstring — Retry-safe key (1-255 printable ASCII; a UUID is recommended; an RFC 8941 quoted string is accepted). The same key with the same request within 24h replays the stored response byte for byte (Idempotent-Replayed: true) without running again; the replay still consumes the operation's rate weight.Responses
POST/api/v1/invoices/{invoice_id}/send
The call validates the draft (total > 0, a brand, and a valid billing email — the invoice's billing_email, else its brand's contact). Written confirmation in chat: the first call (no confirmation_token) sends nothing and answers 200 { status: "confirmation_required", preview, confirmation_token, expires_at } — the preview is exactly what will be sent: invoice number, recipient, every line item with its quantity, unit price and total in integer cents, subtotal, tax, total, currency, due date, sender and the email copy. Show it to the user; only after they confirm in writing call again with the same arguments plus confirmation_token (single use, this credential only, valid 10 minutes). That call sends it once through the app's own send path (the agency's mailbox, else TLNT's invoice sender) with a link to the invoice's secure billing page, marks it sent and answers 200 { status: "executed", action_id, result: { delivery_id, recipient_email } }. A token for another invoice, another credential, expired or already used is refused (409 confirmation_invalid / confirmation_expired / confirmation_used); an invoice edited after the preview (amount, line items, recipient, due date, email) is 409 confirmation_stale — preview again; it is never sent in a form the user did not see. When the credential asks for approval in TLNT for "Sending email", the call is queued for an admin instead (202 pending_approval). Counts one recipient against the daily recipient cap. It moves no money. Requires finance:read + invoices:write + email:send, an Idempotency-Key and an admin member.
send_invoicefinance:read+invoices:write+email:send- admin
- Runs after written confirmation in chat; queued when the credential asks for approval for external_send
- required
- 1
preview.recipient.namepreview.email.text
Input
invoice_id path, requiredstring (uuid) — Invoice id in this workspace.Idempotency-Key header, requiredstring — Retry-safe key (1-255 printable ASCII; a UUID is recommended; an RFC 8941 quoted string is accepted). The same key with the same request within 24h replays the stored response byte for byte (Idempotent-Replayed: true) without running again; the replay still consumes the operation's rate weight.confirmation_token bodystring — Leave out on the first call: it answers status confirmation_required with the exact preview and this token, and sends nothing. Show the preview to the user; only after they confirm in writing, call again with the same arguments plus this token.