ezacto developers

BILL setup

Some clients pay through BILL. This sends those clients their invoice there and brings the payment back, per client, off by default.

What it does

When an invoice is sent to a client who is billed through BILL:

  1. The client becomes a BILL customer, if it is not one already.
  2. The invoice is created in BILL under our own invoice number.
  3. It is delivered — by BILL's own invoice email, or by ezacto's email carrying a BILL payment link. Which one depends on the credential; see below.

Separately, once an hour, a reconciliation reads BILL's receivables and records what settled our invoices: the payment lands on the invoice here as a receipt from BILL, which marks it paid and sends the thank-you like any other. Before it reads the payments it adopts any open invoice of ours that BILL already holds under the same number with the same total -- one sent from Harvest before the cutover, or from here before BILL was configured. Adoption writes only the link, sends nothing, and does not need the client opted in, so a client who pays at BILL closes their invoices here whether or not BILL is how their invoices go out. It also learns the client's BILL customer from the first invoice adopted, when the client has none yet, so an invoice sent from here later reaches the record the client's own BILL account already pays rather than one found by name.

Clients who are not opted in are untouched: their invoice goes out exactly as it does now.

There is no connect button, and that is not an oversight

BILL has no OAuth. There is no authorization code, no consent screen and no token to exchange, so there is nothing a button could start. The credential is deployment configuration, set once.

Credentials

Four values, all required, all secrets:

variable where it comes from
BILL_DEV_KEY Settings → Sync & Integrations → Manage Developer Keys → Generate developer key. Name it for this deployment; the key is revealed by the eye icon and begins 01.
BILL_COMPANY_ID the same page, printed at the bottom as Organization ID; begins 008
BILL_USERNAME see below
BILL_PASSWORD see below

The last two are one choice with real consequences, because BILL reads both from the same two fields:

An AP/AR sync token — the token's name as the username and its value as the password, from Settings → Sync & Integrations → Tokens. This is the safer option: it is scoped, revocable, and BILL refuses it any operation that moves money. It also cannot have BILL send an invoice email:

You do not have permissions for sending an invoice or mailing an invoice.

An operator's BILL login — a real email and password. This can have BILL send its own invoice email. It is a human credential that is not scoped to this integration, so prefer the sync token unless BILL's own email is what you want.

Two optional values:

variable effect
BILL_REPLY_TO_USER_ID a BILL user id beginning 006. Set it and BILL sends its own invoice email with that user as the reply-to; leave it and ezacto sends its invoice email as always, with a BILL payment link in it (%invoice_payment_url% in the template) and the invoice also in BILL for the client's own BILL account to pay.
BILL_ENVIRONMENT sandbox reaches BILL's test organisation. Anything else is the real book, including leaving it unset.

Set all four or none. A deployment with three has a configuration that cannot sign in and an operator who believes it can, so the container refuses to start rather than discovering it on the first invoice.

BILL has no sandbox organisation unless you apply for one, and the developer key is issued by the real organisation, so the usual shape is production only: the values go to the production deployment and the development one is left unconfigured, where the settings page says so. How the values reach a deployment, and how to prove they did, is in Integrations: where the keys go. Once they are there, GET /api/v1/integrations/bill answers configured: true with the company id and environment, and Settings → Integrations shows the same.

Delivery, in more detail

BILL sends ezacto sends
needs an operator login and BILL_REPLY_TO_USER_ID any working credential
the client receives BILL's invoice email ezacto's invoice email, as always, carrying the BILL payment link; the invoice is in BILL for the client's own BILL account to pay too
pays at BILL BILL
payment tracked yes yes

GET /api/v1/integrations/bill reports can_send_from_bill, so the difference is visible rather than discovered.

Turning it on for a client

POST /api/v1/integrations/bill/clients/{clientId}
{ "deliver_via_bill": true }

Administrator only: sending a client's invoice through a third party changes how that client is billed, and it is not a preference set in passing. Turning it on where the deployment has no credentials is refused — a setting that looks saved and delivers nothing is worse than an error.

The invoice is emailed to the contact already marked as that client's invoice recipient. Turning BILL on does not change who gets billed. A client with no such contact is refused rather than sent to nobody.

Sandbox first

BILL's sandbox is free, standalone, and nothing in it touches the real book. Sandbox keys do not work in production and production needs a separate account at bill.com/signup (choose Accounts Payable & Receivable). Production is billed after a 30-day trial.

Point BILL_ENVIRONMENT=sandbox at it and send a test invoice before letting this near a real client.

What it will not do

Retries and duplicates

BILL has no idempotency key on create, so a retried delivery is made safe by our own invoice number: an invoice carrying it is adopted rather than created again. The link is recorded before delivery is attempted, so a failure after the create costs a send and not a second invoice to the client.

The payment reconciliation is a poll. It sees every payment again on every pass, and is keyed on the payment and the invoice together, so the second pass records nothing — and a single BILL payment that settles several invoices is recorded as one row per invoice rather than its whole amount against one.