fn listInvoices(clientId?: string, clientStatus?: string, scope?: string)
GET /invoices (paginated, include=client) → invoices snapshot with
| Argument | Type | Required | Description |
|---|
| clientId? | string | no | |
| clientStatus? | string | no | e.g. "1,2,3,4" |
| scope? | string | no | Optional instance-name qualifier: suffixes the written snapshot |
fn getInvoice(id: string)
GET /invoices/{id}?include=client,payments,activities → invoice.
| Argument | Type | Required | Description |
|---|
| id | string | yes | |
fn listPayments(clientId?: string)
GET /payments (paginated, include=invoices,client) → payments
| Argument | Type | Required | Description |
|---|
| clientId? | string | no | |
fn listExpenses(clientId?: string, vendorId?: string, categoryId?: string, filter?: string)
GET /expenses (paginated, include=vendor,category,client) → expenses
| Argument | Type | Required | Description |
|---|
| clientId? | string | no | |
| vendorId? | string | no | |
| categoryId? | string | no | |
| filter? | string | no | ?filter= (searches custom_value1) |
fn expenseCategories()
GET /expense_categories (paginated) → expenseCategories mapping snapshot.
fn vendors()
GET /vendors (paginated) → vendors mapping snapshot.
fn getClients(ids: array, scope?: string)
GET /clients/{id}?include=contacts per id (batch fan-out, sequential;
| Argument | Type | Required | Description |
|---|
| ids | array | yes | IN client hashids; each validated /^[A-Za-z0-9]+$/. A blank/invalid |
| scope? | string | no | Optional instance-name qualifier: writes the snapshot as |
fn listClients(filter?: string)
GET /clients (paginated; NO include=contacts, optional ?filter=) →
| Argument | Type | Required | Description |
|---|
| filter? | string | no | IN ?filter= search term (name/number/email substring). Query param |
fn clientDomains()
GET /clients?include=contacts (paginated) → clientDomains: the DEDUPED
fn listProjects(clientId?: string, scope?: string)
GET /projects (paginated, include=client; optional client_id filter) →
| Argument | Type | Required | Description |
|---|
| clientId? | string | no | |
| scope? | string | no | Optional instance-name qualifier: suffixes the written snapshot |
fn getProject(id: string)
GET /projects/{id}?include=client → project.
| Argument | Type | Required | Description |
|---|
| id | string | yes | |
fn getCompanySettings(companyId?: string, scope?: string)
GET /companies/{id} -> companySettings: normalized custom-field labels
| Argument | Type | Required | Description |
|---|
| companyId? | string | no | |
| scope? | string | no | Optional instance-name qualifier: writes the snapshot as |
fn upsertExpenses(txns: array, mapping: record, dryRun?: boolean, syncScope?: string)
Check-then-create expenses for a BankTxn[] (dedup on custom_value1).
| Argument | Type | Required | Description |
|---|
| txns | array | yes | BankTxn[] (§6) |
| mapping | record | yes | vendor/category/currency mapping tables (§7.3) |
| dryRun? | boolean | no | Overrides cfg.dryRun; default cfg.dryRun (true). |
| syncScope? | string | no | Caller scope key (e.g. 'mercury-<accountId>' | 'ynab'). Names the |
fn upsertInvoices(invoices: array, dryRun?: boolean, syncScope?: string)
Check-then-create invoices for an InvoiceInput[] (dedup on `number`).
| Argument | Type | Required | Description |
|---|
| invoices | array | yes | InvoiceInput[]: {number (dedup key), clientId, lineItems:[{type: |
| dryRun? | boolean | no | Overrides cfg.dryRun; default cfg.dryRun (true). |
| syncScope? | string | no | Caller scope key; names invoiceSync--<scope> / |
fn applyPayments(payments: array, dryRun?: boolean, syncScope?: string)
Recognize received payments against existing invoices for a
| Argument | Type | Required | Description |
|---|
| payments | array | yes | PaymentInput[]: {clientId (hashid), date:'YYYY-MM-DD', |
| dryRun? | boolean | no | Overrides cfg.dryRun; default cfg.dryRun (true). |
| syncScope? | string | no | Caller scope key; names paymentSync--<scope> / |
fn markSent(invoices: array, dryRun?: boolean, syncScope?: string)
Transition Draft invoices (status_id 1) to Sent (status_id 2) via IN's
| Argument | Type | Required | Description |
|---|
| invoices | array | yes | Targets: [{invoiceId? | invoiceNumber?}] — each resolves to ONE live |
| dryRun? | boolean | no | Overrides cfg.dryRun; default cfg.dryRun (true) -> preview only. |
| syncScope? | string | no | Caller scope key; names markSentSync--<scope> / |
fn invoiceTasks(number: string, clientId: string, taskIds: array, date?: string, dueDate?: string, poNumber?: string, publicNotes?: string, privateNotes?: string, currencyId?: string, status?: enum, rateOverride?: number, dryRun?: boolean, syncScope?: string)
Create ONE draft invoice from a set of IN tasks the way IN's UI does:
| Argument | Type | Required | Description |
|---|
| number | string | yes | Stable dedup key (the next sequential IN invoice number, e.g. 0073); |
| clientId | string | yes | IN client hashid; charset-validated. |
| taskIds | array | yes | >=1 IN task hashids to invoice (explicit — nothing over-included by a |
| date? | string | no | |
| dueDate? | string | no | |
| poNumber? | string | no | |
| publicNotes? | string | no | |
| privateNotes? | string | no | |
| currencyId? | string | no | |
| status? | enum | no | Default 'draft'. 'sent' -> status_id 2 but NEVER emails either way. |
| rateOverride? | number | no | Optional flat rate for every line; else each task's own rate. |
| dryRun? | boolean | no | Overrides cfg.dryRun; default cfg.dryRun (true) -> preview only. |
| syncScope? | string | no | Caller scope key; names invoiceTasksSync--<scope> / |
fn upsertClients(clients: array, dryRun?: boolean, syncScope?: string)
Check-then-create clients for a ClientInput[] (dedup on `number`).
| Argument | Type | Required | Description |
|---|
| clients | array | yes | ClientInput[]: {number (dedup key), name, idNumber?, contacts:[{ |
| dryRun? | boolean | no | Overrides cfg.dryRun; default cfg.dryRun (true). |
| syncScope? | string | no | Caller scope key; names clientSync--<scope> / |
fn upsertProjects(projects: array, dryRun?: boolean, syncScope?: string)
Check-then-create projects for a ProjectInput[] (dedup on `number`).
| Argument | Type | Required | Description |
|---|
| projects | array | yes | ProjectInput[]: {number (dedup key), name, clientId, taskRate?, |
| dryRun? | boolean | no | Overrides cfg.dryRun; default cfg.dryRun (true). |
| syncScope? | string | no | Caller scope key; names projectSync--<scope> / |
fn reconcileCompanySettings(desired?: object, companyId?: string, dryRun?: boolean, syncScope?: string)
Declarative, dry-by-default reconcile of company settings. Diffs a
| Argument | Type | Required | Description |
|---|
| desired? | object | no | Desired company settings. Only keys PRESENT are reconciled; an absent |
| companyId? | string | no | Optional; auto-resolves via GET /companies when absent. Charset-gated. |
| dryRun? | boolean | no | Overrides cfg.dryRun; default cfg.dryRun (true). |
| syncScope? | string | no | Caller scope key; names companySettingsSync--<scope> / |
fn listTasks(clientId?: string, projectId?: string)
GET /tasks (paginated, include=client,project,status; optional
| Argument | Type | Required | Description |
|---|
| clientId? | string | no | |
| projectId? | string | no | |
fn getTask(id: string)
GET /tasks/{id}?include=client,project,status → normalized task row
| Argument | Type | Required | Description |
|---|
| id | string | yes | |
fn upsertTasks(tasks: array, dryRun?: boolean, syncScope?: string)
Check-then-create tasks for a TaskInput[] (dedup on `custom_value1`;
| Argument | Type | Required | Description |
|---|
| tasks | array | yes | TaskInput[]: {number (dedup key), description, clientId?, |
| dryRun? | boolean | no | Overrides cfg.dryRun; default cfg.dryRun (true). |
| syncScope? | string | no | Caller scope key; names taskSync--<scope> / taskSyncErrors--<scope> |
fn upsertVendors(txns: array, dryRun?: boolean)
Auto-create IN vendors for the distinct counterparties of candidate
| Argument | Type | Required | Description |
|---|
| txns | array | yes | BankTxn[] (§6) |
| dryRun? | boolean | no | Overrides cfg.dryRun; default cfg.dryRun (true). |
fn upsertExpenseCategories(txns: array, dryRun?: boolean, fallbackName?: string)
Auto-create IN expense categories for the distinct rawCategories of
| Argument | Type | Required | Description |
|---|
| txns | array | yes | BankTxn[] (§6) |
| dryRun? | boolean | no | Overrides cfg.dryRun; default cfg.dryRun (true). |
| fallbackName? | string | no | Fallback category name always ensured; default 'Uncategorized'. |
fn buildMapping(currencyId: string, fallbackCategoryName?: string, dryRun?: boolean)
PURE derive (no IN fetch): read the latest vendors + expenseCategories
| Argument | Type | Required | Description |
|---|
| currencyId | string | yes | IN currency hashid (USD-only v1, §12.4). |
| fallbackCategoryName? | string | no | Category whose id becomes fallbackCategoryId; default 'Uncategorized'. |
| dryRun? | boolean | no | Overrides cfg.dryRun; default cfg.dryRun (true). Gates the §7.6 |
fn finopsDigest(format?: enum)
Render the daily dry-run into swamp-owned views: summary/detailed
| Argument | Type | Required | Description |
|---|
| format? | enum | no | Which view(s) to write. 'all' (default) writes all three: |
fn archiveExpenses(ids: array, action?: enum, dryRun?: boolean)
Archive (default, reversible) or delete expenses by hashid via
| Argument | Type | Required | Description |
|---|
| ids | array | yes | Expense hashids to act on. |
| action? | enum | no | archive (reversible) or delete (permanent). |
| dryRun? | boolean | no | Overrides cfg.dryRun; default cfg.dryRun (true) → preview only. |
fn archiveTasks(ids: array, action?: enum, dryRun?: boolean)
Archive (default, reversible) or delete tasks by hashid via
| Argument | Type | Required | Description |
|---|
| ids | array | yes | Task hashids (t.id) to act on. |
| action? | enum | no | archive (reversible) or delete (permanent). |
| dryRun? | boolean | no | Overrides cfg.dryRun; default cfg.dryRun (true) → preview only. |
fn archiveClients(ids: array, action?: enum, dryRun?: boolean)
Archive (default, reversible) or delete clients by hashid via
| Argument | Type | Required | Description |
|---|
| ids | array | yes | Client hashids (c.id) to act on. |
| action? | enum | no | archive (reversible) or delete (permanent, cascades to children). |
| dryRun? | boolean | no | Overrides cfg.dryRun; default cfg.dryRun (true) → preview only. |
fn mergeExpenseCategories(merges?: array, deleteIds?: array, deleteEmptied?: boolean, fallbackName?: string, fallbackId?: string, dryRun?: boolean)
Consolidate redundant expense categories: reassign every non-deleted
| Argument | Type | Required | Description |
|---|
| merges? | array | no | Merge groups: reassign fromIds → toId. Default []. |
| deleteIds? | array | no | Categories to archive outright once they hold zero live expenses. |
| deleteEmptied? | boolean | no | After a group's reassignments, also archive the emptied fromIds. |
| fallbackName? | string | no | The buildMapping-ensured fallback category NAME — its id is guarded |
| fallbackId? | string | no | Explicit hashid of the protected fallback category — bypasses the |
| dryRun? | boolean | no | Overrides cfg.dryRun; default cfg.dryRun (true) → preview, no writes. |
fn startTask(id: string, dryRun?: boolean)
Start a task's timer: GET /tasks/{id}, and if no open time_log pair
| Argument | Type | Required | Description |
|---|
| id | string | yes | |
| dryRun? | boolean | no | Overrides cfg.dryRun; default cfg.dryRun (true) → no PUT. |
fn stopTask(id: string, dryRun?: boolean)
Stop a task's timer: GET /tasks/{id}, and if an open time_log pair
| Argument | Type | Required | Description |
|---|
| id | string | yes | |
| dryRun? | boolean | no | Overrides cfg.dryRun; default cfg.dryRun (true) → no PUT. |
Resources
invoices(infinite)— An invoice list snapshot (§4.3)
invoice(infinite)— A single detailed invoice (`/invoices/{id}`)
payments(infinite)— A payment list snapshot (§4.3)
expenses(infinite)— An expense list snapshot (§4.3; carries custom_value1)
expenseCategories(infinite)— Expense-category mapping list (`/expense_categories`)
vendors(infinite)— Vendor mapping list (`/vendors`)
expenseSync(infinite)— One row per txn processed by upsertExpense (§4.4)
expenseSyncErrors(infinite)— Durable audit of failed write attempts (additive, WSPEC-04) — one row
invoiceSync(infinite)— One row per invoice processed by upsertInvoice (invoice-write path);
invoiceSyncErrors(infinite)— Durable audit of failed/rejected invoice writes — one row per invoice
paymentSync(infinite)— One row per payment processed by applyPayment (payment-apply path);
paymentSyncErrors(infinite)— Durable audit of failed/rejected payment applications — one row per
markSentSync(infinite)— One row per invoice processed by markSent (mark-sent path); a Draft
markSentSyncErrors(infinite)— Durable audit of failed/rejected mark-sent transitions — one row per
invoiceTasksSync(infinite)— One row per invoice processed by invoiceTasks (invoice-from-tasks
invoiceTasksSyncErrors(infinite)— Durable audit of failed/rejected invoice-from-tasks writes — one row
clients(1h)— By-id client snapshot (getClients, §1) — deliberately short-lived +
clientList(1h)— Lean client discovery snapshot (listClients, §1) — id/number/name
clientDomains(1h)— Client email-DOMAIN snapshot (clientDomains, §7) — the deduped SET of
clientSync(infinite)— One row per client processed by upsertClient (client-write path); dedup
clientSyncErrors(infinite)— Durable audit of failed/rejected client writes — one row per client
projects(infinite)— A project list snapshot (§2 entity-crud; low-PII — infinite/GC-20).
project(infinite)— A single detailed project (`/projects/{id}`).
projectSync(infinite)— One row per project processed by upsertProject (§2); dedup on `number`.
projectSyncErrors(infinite)— Durable audit of failed/rejected project writes (§2) — flushed even
companySettings(infinite)— A normalized company-settings snapshot (getCompanySettings): custom-
companySettingsSync(infinite)— Per-field reconcile audit (reconcileCompanySettings): one row per diffed
companySettingsSyncErrors(infinite)— Durable audit of a failed company-settings reconcile (resolve/GET/PUT
tasks(infinite)— A task list snapshot (§3; timeLog + derived isRunning)
task(infinite)— A single normalized task (`/tasks/{id}`); same row shape as the list.
taskSync(infinite)— One row per task processed by upsertTask (task-write path); dedup on
taskSyncErrors(infinite)— Durable audit of failed/rejected task writes — one row per task whose
vendorSync(infinite)— One row per distinct vendor processed by upsertVendors (§7.4).
vendorSyncErrors(infinite)— Durable audit of failed vendor creates (§7.4) — flushed even when empty.
categorySync(infinite)— One row per distinct category processed by upsertExpenseCategories (§7.4).
categorySyncErrors(infinite)— Durable audit of failed category creates (§7.4) — flushed even when empty.
categoryMerge(infinite)— Durable audit of a mergeExpenseCategories run (reassignments, archived
mapping(infinite)— The derived Mapping (§7.3) emitted by buildMapping; the sync consumes it
finopsDigest(infinite)— A rendered markdown view (summary|detailed) of the daily dry-run, written
taskTimer(infinite)— Outcome of a startTask/stopTask timer flip (task time_log action, §4).