---
title: "Accounting Playbooks · COUNT Partner API"
description: "Ordered, multi-step COUNT workflows — billing a customer, paying a vendor bill, migrating historical books, closing a month — with the exact tool to call at…"
canonical: "https://developers.getcount.com/guides/playbooks"
source: "https://developers.getcount.com/guides/playbooks"
---
Guides

# Accounting Playbooks

Ordered, multi-step workflows for the operations partners run most: billing a customer, paying a vendor bill, migrating historical books, and closing a month. 9 playbooks, 54 steps, each naming the exact tool to call and the field that usually goes wrong.

The same playbooks your agent already has

These are ported from the workflows the COUNT MCP server serves to agents through `COUNT_playbooks`. An agent calling that tool and a developer reading this page get the same sequence, so you can hand either one the same plan. Each playbook below shows the `COUNT_playbooks` id it corresponds to.

Steps name MCP tool names because that is the shortest way to be unambiguous. The equivalent REST route is derivable from the tool name — `COUNT_list_customers` is `GET /partners/customers` — and the [MCP Server](https://developers.getcount.com/tools/mcp) page documents the mapping in full. The rules behind these steps live in [Ledger Semantics & Lifecycles](https://developers.getcount.com/guides/ledger-semantics).

## Period close

Review a workspace before closing the books on a period.

### Month-end workspace health review

Pull a workspace snapshot, clear open AP and AR, scan for transactions that have not reached the general ledger, then run a trial balance for the closing period.

1. 1 Pull a CFO-style snapshot of cash, receivables, payables, and profitability. `COUNT_get_workspace_stats` query: { include: "cash,receivables,payables,profitability" }.
2. 2 Review draft and unpaid vendor bills. `COUNT_list_bills` query: { approvalStatus: "draft" }, or filter by status and approvalStatus as needed.
3. 3 Review overdue and open customer invoices. `COUNT_list_invoices` Take the pagination and status filters from describe_endpoint for list_invoices.
4. 4 Scan for uncategorized and unreconciled bank transactions in two passes. `COUNT_list_transactions` First query: { uncategorized: true } for register rows that have not posted to the general ledger — add reviewed: true for the ones already signed off, since bank cash and GL cash stay apart until those are categorized. Then query: { reconciled: false } for rows never matched against a statement. Fix categories with change_transaction_category.
5. 5 Run a trial balance for the closing period. `COUNT_generate_trial_balance` query: { startDate, endDate }. Use generate_profit_and_loss instead when you want income-statement review.

Rules behind these steps: [Bank transactions](https://developers.getcount.com/guides/ledger-semantics#transactions) , [Invoices](https://developers.getcount.com/guides/ledger-semantics#invoices) , [Bills](https://developers.getcount.com/guides/ledger-semantics#bills)

Next: Bulk import customers or bank transactions from another system

COUNT_playbooks → month_end_review

## Accounts receivable

Billing a customer, correcting a mistake, and putting a schedule on autopilot.

### Create, approve, send, and collect on an invoice

The full receivable: create the invoice as a draft, approve it so journals post, send it to the customer, then optionally apply a bank deposit as payment.

Where this usually goes wrong

Approving is the step that posts journals, and an empty-body approve call does not always persist. Re-read the invoice before telling anyone it is approved.

1. 1 Resolve the customer and product UUIDs. `COUNT_resolve_references` Pass customerName and/or productName. Prefer this over list_customers/list_products for a pure lookup — those render a user-facing card on every call in agent surfaces.
2. 2 Create the invoice in draft state. `COUNT_create_invoice` body: { customerUuid, date, dueDate, products: [{ productUuid, quantity, unitPrice, description? }], tagUuids? }. dueDate is effectively required for invoiceType "invoice" and "estimate" — 400 without it, despite being schema-optional.
3. 3 Approve the draft so journals post, then verify it took. `COUNT_approve_invoice` id: the invoice UUID from the create response. Follow with get_invoice and check approved/isDraft rather than trusting the 200 — an empty-body approve can be a no-op.
4. 4 Send the invoice to the customer. `COUNT_send_invoice` id: the same invoice UUID. Optional body: to (defaults to the customer email), subject, message, sendCopy. cc, bcc, attachPdf, and recipients are not real fields on this route.
5. 5 Apply a bank deposit as payment.Optional `COUNT_assign_transaction_to_bills_invoices` id: the Income transaction UUID. body: { matchingType: "invoice", records: [{ id: "<invoice-uuid>", paymentAmount }] }.

Rules behind these steps: [Invoices](https://developers.getcount.com/guides/ledger-semantics#invoices)

Next: Pay a vendor bill with a bank transaction , Apply a credit memo to an invoice, and fix an approved invoice

COUNT_playbooks → create_invoice_and_send

### Apply a credit memo to an invoice, and fix an approved invoice

There is no revert-to-draft route, so correcting an approved invoice means applying a credit memo rather than editing the original.

Where this usually goes wrong

The memo must belong to the same customer as the invoice. A memo raised against the wrong customer can never be applied — check customerUuid at creation, not at application.

1. 1 Create the credit memo for the same customer as the invoice you need to correct. `COUNT_create_invoice` body: { invoiceType: "memo", customerUuid: "<same customer as the target invoice>", date, products: [...] }. Cross-customer application always fails later, so double-check customerUuid now.
2. 2 Approve the memo — memos follow the same draft → approved lifecycle as invoices. `COUNT_approve_invoice` id: the memo UUID from step 1. Verify with get_invoice afterwards.
3. 3 Apply the approved memo to the target invoice. `COUNT_apply_multiple_credits_to_single_invoice` id: the target invoice UUID. body: { creditMemos: [{ id: "<memo-uuid>", amount }] }. Both documents must be approved and share a customer, and the memo must not already be fully applied — 400 otherwise.
4. 4 Confirm the invoice open balance dropped by the applied amount. `COUNT_get_invoice` The same invoice UUID as step 3.

Rules behind these steps: [Credit memos](https://developers.getcount.com/guides/ledger-semantics#credit-memos) , [Invoices](https://developers.getcount.com/guides/ledger-semantics#invoices)

Next: Create, approve, send, and collect on an invoice

COUNT_playbooks → apply_credit_memo_to_invoice

### Create a recurring invoice template and actually make it active

New templates default to isDraft: true and stay invisible and inactive even after being resumed. This walks through the extra step that is easy to miss.

Where this usually goes wrong

resume_recurring_invoice_template does not clear isDraft. Only update_recurring_invoice_template with isDraft: false activates a template.

1. 1 Resolve the customer and product UUIDs. `COUNT_resolve_references` Pass customerName and/or productName rather than listing customers and products.
2. 2 Create the template. `COUNT_create_recurring_invoice_template` body: { customerUuid, date, dueDate, products: [...], recurrencePattern }. The response comes back with isDraft: true — the default — and the template generates nothing in that state.
3. 3 Activate the template by clearing isDraft explicitly. Do not skip this step. `COUNT_update_recurring_invoice_template` id: the template UUID from step 2. body: { isDraft: false, nextInvoiceDate: "<first scheduled date>" }. Always include nextInvoiceDate on this and every future update — omitting it silently nulls the value out and pauses the schedule.
4. 4 Confirm the template is now active and visible. `COUNT_list_recurring_invoice_templates` This tool only ever shows isDraft: false templates. If the template you just activated is missing, re-check step 3 rather than assuming a listing bug — get_recurring_invoice_template works regardless of isDraft and is the better debugging call.

Rules behind these steps: [Recurring invoice templates](https://developers.getcount.com/guides/ledger-semantics#recurring-invoice-templates) , [Invoices](https://developers.getcount.com/guides/ledger-semantics#invoices)

Next: Create, approve, send, and collect on an invoice

COUNT_playbooks → activate_recurring_invoice_template

## Accounts payable

Settling a vendor bill against a real bank transaction.

### Pay a vendor bill with a bank transaction

Find an approved bill, locate or create the matching Expense transaction, and apply it as payment.

Where this usually goes wrong

Bills accept Expense transactions only. An Income transaction against a bill always 400s, and vendor credit memos go through apply_vendor_memos_to_bill instead.

1. 1 List approved bills for the vendor or period you want to pay. `COUNT_list_bills` query: { approvalStatus: "approved", vendorUuids: "<vendor-uuid>", page: 1, limit: 50 }. Use vendorUuids from list_vendors — the numeric vendors filter is rejected.
2. 2 Load the bill detail and confirm amountDue, currency, and billType. `COUNT_get_bill` id: the bill UUID from the list_bills row id field.
3. 3 Find an existing Expense transaction to apply, or create one if the payment is new. `COUNT_list_transactions` Filter for unreconciled Expense rows matching the amount and date. Otherwise use create_transaction with type Expense, accUuid, categoryAccountUuid, amount, and postedDate.
4. 4 Apply the transaction to the bill. `COUNT_assign_transaction_to_bills_invoices` id: the transaction UUID. body: { matchingType: "bill", records: [{ id: "<bill-uuid>", paymentAmount }] }. The bill must be approved and the transaction must be Expense type.
5. 5 Reload the bill and confirm paidAmount and amountDue updated.Optional `COUNT_get_bill` The same bill UUID as step 2.

Rules behind these steps: [Bills](https://developers.getcount.com/guides/ledger-semantics#bills) , [Bank transactions](https://developers.getcount.com/guides/ledger-semantics#transactions) , [Vendors](https://developers.getcount.com/guides/ledger-semantics#vendors)

Next: Month-end workspace health review

COUNT_playbooks → pay_vendor_bill

## Onboarding and migration

Standing up a new workspace and moving historical books across.

### Set up a chart of accounts and import historical transactions

Confirm scope, inventory existing accounts, create everything missing, resolve references, preflight each batch, import, then verify the totals with a report.

Where this usually goes wrong

Finish the whole chart of accounts before importing anything. Once an account has journal entries it can no longer be deleted, only deactivated.

1. 1 Confirm which workspace you are setting up and importing into. `COUNT_auth_status` When more than one workspace is authorized, also call list_workspaces and pass workspace_id on every subsequent call.
2. 2 Inventory existing bank, cash, income, and expense accounts for import mapping. `COUNT_list_accounts` query: { search: "<name fragment>", type: "Expenses" } for categories; omit type for bank and cash accounts. Copy the id UUIDs for accUuid and categoryAccountUuid.
3. 3 Resolve the sub-type for each missing account type before creating anything. `COUNT_list_account_sub_types` Filter query.type by Assets (bank), Liabilities (credit card), Income, or Expenses. Copy the matching row’s integer id — never guess subTypeId values.
4. 4 Create every missing bank, credit card, and category account. Complete the full chart of accounts before importing bills or transactions. `COUNT_bulk_create_accounts` body: { accounts: [{ name, subTypeId }, ...] } — the same shape as create_account per row. Chunk at 100 rows per call, ~25 recommended. Retry only rows where success is false.
5. 5 Resolve source-system vendor, customer, and account names to UUIDs. `COUNT_resolve_references` Pass vendorName, customerName, customerEmail, accountName, and accountType as needed.
6. 6 Re-fetch account UUIDs for anything created in step 4. `COUNT_list_accounts` query: { search: "<account name>" }. Copy the id UUIDs into your import rows.
7. 7 Preflight each batch payload before calling a bulk create tool. `COUNT_validate_payload` toolName: COUNT_bulk_create_transactions or COUNT_bulk_create_journal_entries. Set verifyReferences: true to confirm the UUIDs exist.
8. 8 Import in batches of ~25 rows, and retry only the failed row indices. `COUNT_bulk_create_transactions` Bank register rows go through bulk_create_transactions; GL-only historical postings through bulk_create_journal_entries. Hard cap is 100 rows per call. Read errorCount first.
9. 9 Spot-check a sample of imported rows for dates, amounts, and categories. `COUNT_list_transactions` Filter to the imported date range, and use get_transaction for individual row detail.
10. 10 Sanity-check the totals with a profit and loss report for the imported period. `COUNT_generate_profit_and_loss` query: { startDate, endDate, basis: "accrual" } under the top-level query key.

Rules behind these steps: [Chart of accounts](https://developers.getcount.com/guides/ledger-semantics#accounts) , [Bank transactions](https://developers.getcount.com/guides/ledger-semantics#transactions) , [Manual journal entries](https://developers.getcount.com/guides/ledger-semantics#journal-entries)

Next: Bulk import customers or bank transactions from another system , Month-end workspace health review

COUNT_playbooks → setup_accounts_and_import_transactions

### Bulk import customers or bank transactions from another system

Confirm workspace scope, map accounts, resolve name references, preflight each batch, import in ~25-row chunks, then sanity-check with a P&L.

Where this usually goes wrong

Bulk calls return HTTP 201 when the envelope is accepted, even if every row inside it failed. Read errorCount before treating an import as done.

1. 1 Confirm which workspace you are importing into. `COUNT_auth_status` When more than one workspace is authorized, also call list_workspaces and pass workspace_id on every subsequent call.
2. 2 Map bank accounts and income/expense category accounts in the chart of accounts. `COUNT_list_accounts` query: { search: "<name fragment>", type: "Expenses" } for categories; omit type for bank and cash accounts.
3. 3 Resolve vendor, customer, and account names to UUIDs before building bulk rows. `COUNT_resolve_references` Pass vendorName, customerName, customerEmail, accountName, and accountType as needed.
4. 4 Preflight each batch payload before calling a bulk create tool. `COUNT_validate_payload` toolName: COUNT_bulk_create_transactions or COUNT_bulk_create_customers. body: { transactions: [...] }. Set verifyReferences: true to confirm the UUIDs exist.
5. 5 Import in batches of ~25 rows, and retry only the failed row indices. `COUNT_bulk_create_transactions` body: { transactions: [<same shape as create_transaction>] }. Hard cap 100 rows per call. Read errorCount first, then retry rows where success is false.
6. 6 Sanity-check the totals with a profit and loss report for the imported period. `COUNT_generate_profit_and_loss` query: { startDate, endDate, basis: "accrual" } under the top-level query key.

Rules behind these steps: [Bank transactions](https://developers.getcount.com/guides/ledger-semantics#transactions) , [Chart of accounts](https://developers.getcount.com/guides/ledger-semantics#accounts)

Next: Set up a chart of accounts and import historical transactions , Month-end workspace health review

COUNT_playbooks → migration_import

## Budgeting

Planning a budget and reviewing it against actuals.

### Plan a budget and review actuals against it

Reuse or create a draft budget, load planned amounts, publish, then compare actual and budget columns in the grid.

Where this usually goes wrong

Create the budget with actualPeriods at least 1, otherwise the grid has no trailing actual columns to review variance against.

1. 1 Check existing budgets and the workspace Overall Budget before creating a new plan. `COUNT_list_budgets` Optional query.status: draft or published. get_overall_budget shows whether an Overall Budget is already configured.
2. 2 Reuse a draft budget from step 1, or create one when none exists for the period. `COUNT_create_budget` Skip this when a usable draft already exists — carry that budget UUID and draft versionNumber forward. Otherwise body: { name, startPeriod, cadence, actualPeriods, budgetPeriods } with actualPeriods >= 1.
3. 3 Export the budget grid structure for planning. `COUNT_get_budget_grid` id: the budget UUID. query: { includeActuals: "false", versionNumber: <draft version> }.
4. 4 Resolve account names to UUIDs when building rows from a spreadsheet. `COUNT_resolve_references` Pass accountName plus accountType (Income or Expenses).
5. 5 Preflight each import batch before writing budget cells. `COUNT_validate_payload` toolName: COUNT_bulk_update_budget_cells. body: { updates: [{ accountUuid, periodStart, amount }] }.
6. 6 Load planned amounts in batches of ~25 rows, and retry only failed indices. `COUNT_bulk_update_budget_cells` id + versionNumber + body.updates[]. Use periodStart values from the get_budget_grid columns. Hard cap 100 rows.
7. 7 Publish the budget once the planned amounts are final. `COUNT_publish_budget` id: the budget UUID. Optional body.versionNumber for the draft version to publish.
8. 8 Review actuals against budget by account and period. `COUNT_get_budget_grid` query: { includeActuals: "true", versionNumber: <published version> }. Optional query.reportType: accrual or cash.
9. 9 Deep-dive a specific period or category with a P&L report.Optional `COUNT_generate_profit_and_loss` query: { startDate, endDate, basis: "accrual" }.

Rules behind these steps: [Chart of accounts](https://developers.getcount.com/guides/ledger-semantics#accounts)

Next: Export, edit, and re-import a P&L budget , Month-end workspace health review

COUNT_playbooks → plan_budget_and_review_actuals

### Export, edit, and re-import a P&L budget

Export the budget grid as JSON, edit the amounts offline in a spreadsheet, then bulk-import the cell updates onto a draft version.

Where this usually goes wrong

Partner budget tools are JSON-only — there is no CSV upload. Keep accountUuid, periodStart, and amount columns aligned with the exported grid.

1. 1 List budgets, or create a new draft budget for the planning period. `COUNT_list_budgets` Optional query.status: draft. Otherwise create_budget with name, startPeriod, cadence, actualPeriods, and budgetPeriods.
2. 2 Export the budget grid with account UUIDs and period columns. `COUNT_get_budget_grid` id: the budget UUID. query: { includeActuals: "false", versionNumber: <draft version> } for a faster budget-only export.
3. 3 Resolve account names to UUIDs when building import rows from the spreadsheet. `COUNT_resolve_references` Pass accountName plus accountType (Income or Expenses).
4. 4 Preflight each import batch before writing cells. `COUNT_validate_payload` toolName: COUNT_bulk_update_budget_cells. body: { updates: [{ accountUuid, periodStart, amount }] }.
5. 5 Import the edited amounts in batches of ~25 rows, and retry only failed indices. `COUNT_bulk_update_budget_cells` id + versionNumber + body.updates[]. Use periodStart values from the get_budget_grid columns. Read errorCount first.
6. 6 Publish the budget when the amounts are final. `COUNT_publish_budget` id: the budget UUID. Optional body.versionNumber for the draft version to publish.

Rules behind these steps: [Chart of accounts](https://developers.getcount.com/guides/ledger-semantics#accounts)

Next: Plan a budget and review actuals against it , Month-end workspace health review

COUNT_playbooks → budget_import
