Build Embedded Accounting in 20 Minutes

By the end of this guide you will have extended your chart of accounts, invoiced a customer for real money, recorded the payment, and read the resulting double-entry records back from the ledger — entirely over the API.

That last part is the point. Most accounting integrations stop at "the invoice was created." This walkthrough keeps going until you can see what the ledger did about it, because that's the part your customers' accountants will trust you on.

What you need:

  • A sandbox company — sign up and create your company in that flow
  • App Connect credentials (client_id / client_secret) from Company settings in the workspace
  • curl or any HTTP client
  • 20 minutes

Every request below uses two headers. Export them once:

Shell
export ACCESS_TOKEN="<from the credential exchange>"
export BASE="https://api-sandbox.paprel.com"
export AUTH="authorization: Bearer $ACCESS_TOKEN"
export CT="content-type: application/json"

First time? The five-minute setup — token exchange and request signing — is on the Authentication page. The curl examples below show the auth header only; write requests also require an x-data-signature header, which the SDK computes for you.

All paths are relative to the sandbox API base URL. The exact request and response schemas for every endpoint here are in the API reference.


Step 1 — Look at (and extend) the chart of accounts

You don't start from zero: the company already has a working chart of accounts. Read it first.

Shell
curl $BASE/v1/accounting/accounts -H "$AUTH"

Endpoint reference: GET /v1/accounting/accounts

Now add an account of your own — say, a dedicated revenue account for design services, nested under the income tree:

Shell
curl -X POST $BASE/v1/accounting/accounts \
  -H "$AUTH" -H "$CT" \
  -d '{
    "account": "Design Services Revenue",
    "kind": "…",
    "account_subtype": "…",
    "currency": "USD",
    "is_sub_account": true,
    "parent_id": "…"
  }'

Endpoint reference: POST /v1/accounting/accounts

Use a parent_id from the GET response in the previous call, and a kind/account_subtype matching the income branch you're nesting under.

Behind the scenes: the chart of accounts is the spine of every report you'll see in step 7. When the invoice in step 4 posts revenue, it posts to an account in this tree — which is why the P&L can group, subtotal, and compare without any reporting configuration on your side. Hierarchies (parent/child accounts) are how detailed books stay readable.

The seeded chart, rendered in the workspace: coded accounts grouped by type — assets, equity, expenses, liabilities, revenue — with the hierarchy your step-1 account slots into.

Step 2 — Create a customer

Shell
curl -X POST $BASE/v1/clients \
  -H "$AUTH" -H "$CT" \
  -d '{
    "company_name": "Brightline GmbH",
    "company_email": "billing@brightline.example",
    "contact_first_name": "Jonas",
    "contact_last_name": "Weber",
    "currency": "USD",
    "payment_term": "…"
  }'

Endpoint reference: POST /v1/clients

Keep the returned people_id — the invoice in step 4 references it.

Behind the scenes: a customer record is more than a name on a PDF. It anchors the receivables sub-ledger: every invoice, credit note, and payment for this customer links back to it, which is how "what does Brightline owe us?" becomes a single indexed read instead of a report you assemble by hand.

Step 3 — Create a product

Shell
curl -X POST $BASE/v1/items \
  -H "$AUTH" -H "$CT" \
  -d '{
    "item_name": "Brand identity package",
    "item_type": "service",
    "sku": "DSN-001",
    "sales_price": "4800.00",
    "sales_currency": "USD",
    "sales_account_id": "…",
    "sales_description": "Logo system, typography, and brand guidelines"
  }'

Endpoint reference: POST /v1/items

Set sales_account_id to the "Design Services Revenue" account you created in step 1 — this is the link that makes revenue land in the right place automatically.

Behind the scenes: the item carries its accounting treatment with it. Anyone (or any agent) who invoices this item never has to know which ledger account revenue belongs in — the mapping was decided once, here, by whoever understood the books.

Step 4 — Create an invoice

Now the pieces compose:

Shell
curl -X POST $BASE/v1/invoices \
  -H "$AUTH" -H "$CT" \
  -d '{
    "people_id": "<people_id from step 2>",
    "currency": "USD",
    "document_issue_date": "2026-06-12",
    "document_due_date": "2026-07-12",
    "items": [
      {
        "item_id": "<item id from step 3>",
        "item_name": "Brand identity package",
        "item_quantity": "1",
        "item_cost": "4800.00"
      }
    ]
  }'

Endpoint reference: POST /v1/invoices

The response includes the invoice's anchor_id — the stable identifier that survives edits. Use it for everything that follows. (Invoices are versioned internally; every edit creates a new version while the anchor stays constant. That's what makes the audit trail in step 7 possible.)

Behind the scenes — this is where double-entry starts working for you. Posting this invoice generates a balanced journal:

AccountDebitCredit
Accounts Receivable4,800.00
Design Services Revenue4,800.00

You didn't write that journal. The invoice did — through the account mapping from step 3. Debits equal credits or the ledger rejects the posting; there is no code path that lets an unbalanced entry through.

The invoice in the workspace. Note the activity panel on the right: drafted, revised v1 → v2 → v3, status transitioned draft → sent — every step you take over the API lands in this same history.

Step 5 — Send the invoice

Shell
curl -X POST $BASE/v1/invoices/<anchor_id>/mail/send \
  -H "$AUTH" -H "$CT"

Behind the scenes: delivery is logged on the document's activity history alongside every other action — created, edited, sent, paid — so the full lifecycle is reconstructible later. If your platform needs an approval gate before sending (draft → reviewed → approved), invoice statuses are configurable via the status settings endpoints, and status changes land in the same audit history.

Step 6 — Receive the payment

Brightline pays. Record it against the invoice:

Shell
curl -X POST $BASE/v1/invoices/<anchor_id>/payment \
  -H "$AUTH" -H "$CT" \
  -d '{
    "payment_amount": "4800.00",
    "payment_date": "2026-06-12",
    "payment_mode": "…",
    "deposit_account": "<a bank/cash account id from step 1 GET>",
    "reference_number": "WIRE-20260612-001"
  }'

Endpoint reference: POST /v1/invoices/:anchor_id/payment

Behind the scenes: a second journal posts, and the books update everywhere at once:

AccountDebitCredit
Cash · Operating4,800.00
Accounts Receivable4,800.00

Receivables for Brightline drop to zero, cash rises, revenue is untouched (it was earned in step 4 — that's accrual accounting working correctly), and the invoice flips to paid. One API call, four consistent facts.

Retried the request because of a timeout? The write path is idempotent — resubmitting returns the original result instead of double-paying the invoice.

Step 7 — Read the ledger back

Here's the proof. Ask the ledger what happened:

Shell
curl "$BASE/v1/accounting/journals" -H "$AUTH"

Endpoint reference: GET /v1/accounting/journals

The response is the journal list — you'll recognize both entries from steps 4 and 6, each with balanced lines against the accounts in your chart. Every report in the workspace — P&L, balance sheet, trial balance — is generated from exactly these rows, live:

The general ledger, by account: every journal carries an identifier and links back to the document that generated it — invoices, bills, expenses.

  • P&L now shows 4,800.00 in Design Services Revenue

The income statement, generated live from those journal rows on an accrual basis — no sync step, no reconciliation job.

  • Balance sheet shows the cash in Cash · Operating, receivables back at zero
  • Trial balance ties to zero — it always does, because every posting was balanced at write time

Open the workspace UI and look at the reports to see it rendered. Then run the auditor's question against the API: who created the invoice, when was it sent, when was it paid, what did each step post? Every answer is in the activity history and journal lines you just created.


What you just built

In seven API calls you exercised the full loop most platforms take quarters to build in-house: a programmable chart of accounts, customer and item records that carry their accounting treatment, document-driven journal generation, idempotent payment recording, and reports that are derived from the ledger instead of reconciled against it.

Need to provision companies for your customers in production? See White-label platform setup.

Everything you did over REST is also exposed as MCP tools for AI agents — same primitives, scoped tokens, every agent action landing as a reviewable draft.

Where to go next:

Evaluate Paprel

Build in sandbox, launch with a production trial

Use sandbox for developer testing with no billing. When you are ready for real workflows, start production on a monthly plan with a 14-day free trial.

API-First Delivery
Audit-Ready Controls
Sandbox And Guided Rollout
We use cookies to improve your experience. Manage preferences or accept all.