The primary confirmation path. The portal returns with a session id, the server looks that session up with the business's own credentials, checks its metadata actually names this invoice and this org, and records the payment using the amount the processor reports — never a figure supplied by the browser.
Also called: invoice didn't update after paying · customer paid but it still says unpaid · payment confirmation
- 1POST /api/client-portal/invoice/confirm takes the portal token plus the session id.
- 2The session's metadata.invoice_id and metadata.org_id must match the token's invoice, or it is refused with a 403.
- 3An unsettled session (async bank debit still clearing) returns paid:false and leaves it to the webhook or the sweep.
- 4Recording is idempotent on the session id, so a webhook racing this can never double-credit.
A customer who pays and then sees the invoice still marked unpaid assumes it failed, and either pays again or picks up the phone. The invoice is confirmed the moment they return from the payment page, which means it works on any setup with nothing more than the processor's key in place — no extra plumbing to configure. The confirmation only counts if the payment session matches that invoice and that business, and the amount is read from the processor rather than from the browser, so a stray link cannot mark somebody else's invoice paid.
- Invoices staying unpaid because a webhook was never configured or could not reach the environment.
- A guessed session id being used to mark an unrelated invoice paid.
See it on your own jobs
Twenty minutes, your numbers, no slide deck. We’ll build one of your real buildings in front of you and send you the estimate link at the end — yours to keep either way.
or keep browsing features →