Import a Bill from an Unlinked Account

Import a bill from an unlinked account

Use Bill Imports when the customer has a bill document but has not connected that biller account. BillerAPI creates a normalized Bill after review. If the biller is supported, prompt the customer to connect so future bills arrive automatically.

No account connection is required

The customer can import this bill before linking an account. After extraction, show the resolved biller and whether Connect is available.
  1. 01

    Create an import and receive a short-lived upload target

  2. 02

    PUT the original document directly to the upload target

  3. 03

    Complete the upload and wait for NEEDS_REVIEW or READY_TO_COMMIT

  4. 04

    Review candidates and evidence; patch corrections when needed

  5. 05

    Confirm with the expected draft and lifecycle versions

  6. 06

    Store committed_bill_id and consume bill.created

Create and upload

Node
import { createHash } from 'node:crypto';
import { readFile } from 'node:fs/promises';

const bytes = await readFile('utility-bill.pdf');
const sha256 = createHash('sha256').update(bytes).digest('hex');
const billImport = await billerapi.billImports.create({
  client_user_id: 'user_123',
  file_name: 'utility-bill.pdf',
  media_type: 'application/pdf',
  declared_bytes: bytes.length,
  sha256,
});

await fetch(billImport.upload_target!.url, {
  method: 'PUT', headers: billImport.upload_target!.headers, body: bytes,
});

Review is a product boundary, not an error state

Extraction confidence and source evidence are exposed so your UI can ask a human to verify uncertain values. Confirmation uses optimistic versions to prevent committing a stale draft.

Confirm, then choose the honest next action

Branch on the committed Bill's payment route. The durable goal is to connect a supported account for future sync, not merely to unlock payment.

Node
const result = await billerapi.billImports.confirm(importId, review);
const { bill } = result;

switch (bill.payment_route?.type) {
  case 'ONBOARDED_LINKED':
    return showConnectedBill(bill);
  case 'ONBOARDED_CONNECTION_REQUIRED':
    return showConnectAccount({
      billerId: bill.biller?.biller_id ?? bill.payee?.canonical_biller_id,
      billId: bill.id,
    });
  default:
    return showExternalOnlyBill(bill);
}
API statePrimary actionWhat happens next
ONBOARDED_LINKEDView bill; schedule payment if eligibleFuture bills continue to sync.
ONBOARDED_CONNECTION_REQUIREDConnect accountFuture bills and eligible biller messages flow through the connection.
EXTERNAL_ONLYTrack bill or mark paid externallyThe biller cannot be connected yet; never imply automated payment.
Was this page helpful?