Quick start
From docker run to a webhook on your machine and a month skipped. Every call below runs as written against a fresh container.
-
Run it
Pin the API key, or a fresh volume prints a new one at boot (in a shell, single quotes keep the
$). Start the clock in a virtual mode, or it follows the machine and refuses to skip.docker run --rm -p 45445:45445 \ -e 'ASAAS_API_KEY=$aact_hmlg_local' \ -e CLOCK_MODE=VIRTUAL_FLOWING \ -e WEBHOOK_LOCALHOST_REWRITE=host.docker.internal \ --add-host host.docker.internal:host-gateway \ ghcr.io/instituto-ama/asaas-mock:latestWEBHOOK_LOCALHOST_REWRITElets a webhook registered ashttp://localhost:4000/…reach your machine from inside the container. The panel is at http://localhost:45445. -
Point your application at it
Switching from the sandbox to the mock is switching the base URL. The paths, bodies, headers and errors are Asaas's.
- ASAAS_BASE_URL=https://api-sandbox.asaas.com/v3 + ASAAS_BASE_URL=http://localhost:45445/v3 ASAAS_API_KEY=$aact_hmlg_local -
Create a customer
curl localhost:45445/v3/customers \ -H 'access_token: $aact_hmlg_local' -H 'content-type: application/json' \ -d '{"name":"Maria Souza","cpfCnpj":"24971563792","email":"maria@example.com","mobilePhone":"11987654321"}' # → {"object":"customer","id":"cus_526128665490",…} -
Register your webhook
Through the real
/v3/webhooks. The rules are Asaas's: theauthTokenneeds 32 to 255 characters, and your endpoint must answer HTTP 200.curl localhost:45445/v3/webhooks \ -H 'access_token: $aact_hmlg_local' -H 'content-type: application/json' \ -d '{"name":"my-app","url":"http://localhost:4000/webhooks/asaas","email":"dev@example.com", "enabled":true,"interrupted":false,"apiVersion":3,"sendType":"SEQUENTIALLY", "authToken":"whsec_local_demo_token_0123456789abcdef", "events":["PAYMENT_CREATED","PAYMENT_CONFIRMED","PAYMENT_RECEIVED","PAYMENT_OVERDUE"]}'Each delivery carries the
asaas-access-tokenheader with your token.GET /_admin/webhooks/deliveriesshows every attempt with your endpoint's answer. -
Charge and pay
A Pix charge goes straight to
RECEIVEDwhen paid. Pay it like the payer would, on the page atinvoiceUrl(a real QR code), or with the sandbox's own call:curl localhost:45445/v3/payments \ -H 'access_token: $aact_hmlg_local' -H 'content-type: application/json' \ -d '{"customer":"cus_526128665490","billingType":"PIX","value":89.9,"dueDate":"2026-11-10"}' curl -X POST localhost:45445/v3/sandbox/payment/pay_…/confirm -H 'access_token: $aact_hmlg_local' # → your endpoint receives PAYMENT_CREATED, then PAYMENT_RECEIVED -
Skip time
Every simulated day runs a full tick: subscriptions charge, unpaid charges go overdue, cards credit, webhooks fire.
curl localhost:45445/_admin/clock # where the clock is curl -X POST localhost:45445/_admin/clock/advance \ -H 'content-type: application/json' -d '{"days": 32}' curl -X POST localhost:45445/_admin/clock/set \ -H 'content-type: application/json' -d '{"date": "2027-01-01"}'The use cases put this to work: a card that credits at D+32, a boleto paid late, a subscription a year ahead.
-
In your compose
services: asaas: image: ghcr.io/instituto-ama/asaas-mock:0.11.0 # pin a version, or :latest ports: - '45445:45445' environment: # `$$` is a literal `$` in compose. ASAAS_API_KEY: '$$aact_hmlg_local' WEBHOOK_LOCALHOST_REWRITE: host.docker.internal CLOCK_MODE: VIRTUAL_FLOWING extra_hosts: - 'host.docker.internal:host-gateway' # Linux volumes: - asaas-data:/data volumes: asaas-data:From a service in the same network, the mock is
http://asaas:45445/v3. Put awhatsapp, amailpitand ansmsservice next to it and it finds them by itself, as on the Asaas page. -
In CI
Use
CLOCK_MODE=VIRTUAL_FROZENand no volume: the clock moves only when the test says so, and every run starts from the same state. A test then reads like the business rule it checks:# create the charge → assert PENDING # advance 1 day after the due date → assert your order is marked overdue # confirm the payment → assert the webhook handler settled the order