A read-only dLocal investigation and triage CLI built for AI agents.
dLocal is an emerging-markets payment processor (LatAm, Africa, Asia; payins and payouts).
agent-dlocal turns "why did this payment fail?", "where did this payout go?", and "what
happened to this refund?" into single commands that emit compact, structured, redacted output — so
an LLM can answer them without ever seeing a credential.
It is the dLocal member of the agent-* family and shares its contracts with agent-stripe.
brew install shhac/tap/agent-dlocal
agent-dlocal auth add prod --form
agent-dlocal auth check
--form collects the X-Login, X-Trans-Key, and Secret key through native OS dialogs — one per
secret, each titled with the value it wants — so the secrets go straight from the user's keyboard
into the OS keychain. They never appear in a chat transcript and never pass through a model's
context. Values already given by flag are not prompted for again.
If you are automating, --login/--trans-key/--secret-key exist — but prefer --form for
anything a human is present for.
Sandbox, and optional mutual TLS:
agent-dlocal auth add sbox --sandbox --form
agent-dlocal auth add prod --cert ~/.dlocal/client.pem --key ~/.dlocal/client.key --form
--cert/--key take paths; the files stay where you put them under your own permissions. The
client key must be unencrypted PEM — Go's TLS loader cannot decrypt a passphrase-protected key,
so decrypt it first with openssl pkey -in key.pem -out key-decrypted.pem.
dLocal keys carry no test/live marker, so live vs sandbox is a host distinction recorded
explicitly on the profile — nothing is guessed from the credential.
Start from the question, not the endpoint:
agent-dlocal investigate payment D-4-8f2a... # Why did this payment fail?
agent-dlocal investigate order ORDER-10241 # They say they paid; our order says unpaid
agent-dlocal investigate refund REF-4471 # What happened to this refund?
agent-dlocal investigate payout P-2-91bc... # Where is this payout?
Each returns a verdict, a terminal flag saying whether the state is final, next_steps, and the
evidence it drew on.
Direct retrieval when you know what you want:
agent-dlocal payments get D-4-aaa D-4-bbb # multiple ids, one record each
agent-dlocal payments status D-4-aaa # status triple only
agent-dlocal orders get ORDER-10241 # merchant reference -> payment
agent-dlocal refunds get REF-4471
agent-dlocal chargebacks get CHAR42342
agent-dlocal payouts get P-2-91bc
agent-dlocal payment-methods list PH VN TH # several markets at once
agent-dlocal payment-methods countries --supported # which markets work at all
agent-dlocal api get /payments/D-4-aaa # GET-only escape hatch
agent-dlocal usage prints the whole map; each group has its own usage too.
--country is a global flag, so switching market is the same flag on every command that takes one:
agent-dlocal --country PH auth check
agent-dlocal --country PH payment-methods list
payment-methods also takes countries positionally and repeatably, mirroring the multi-id get
contract — one record per country, in input order:
$ agent-dlocal payment-methods list PH VN TH
{"country":"PH","count":14,"payment_methods":[...]}
{"country":"VN","count":14,"payment_methods":[...]}
{"country":"TH","count":13,"payment_methods":[...]}
Precedence is positional argument → --country → the profile's stored country. A country the
merchant is not enabled for becomes a record carrying reason, so asking about ten markets and
having one fail still answers the other nine.
dLocal has no list-countries endpoint, so payment-methods countries probes each market and reports
which resolve for your credentials — 43 markets in about a second:
$ agent-dlocal payment-methods countries --supported
{"country":"BR","supported":true,"payment_methods":49}
{"country":"MX","supported":true,"payment_methods":37}
...
Supported markets span South and Central America, South and Southeast Asia (including the Philippines, Indonesia, Vietnam, Thailand, Malaysia, India, Japan), and much of Africa. dLocal does not support Singapore, South Korea, Taiwan, Hong Kong, Venezuela, or western Europe.
Every command is a GET. The raw api group has no --method flag — the read-only guarantee is
the absence of a code path, not a check that could later be relaxed. dLocal refunds and payouts move
real money in markets where reversal is slow, manual, or impossible.
- Lists stream NDJSON by default;
--format json|yamlavailable. get <id>...returns one record per id in input order. A miss emits an@unresolvedline on stdout with exit 0, so one bad id does not lose the batch. Only command-level failures go to stderr with exit 1.- Sensitive fields are redacted by default — including
payer.document, which is a national ID number (CPF, CUIT, DNI).--expose <path,key>opts out per invocation. Stored credentials are never exposable. - Errors are JSON on stderr:
{"error", "fixable_by": "agent"|"human"|"retry", "hint"}.
dLocal reports a triple — status, status_code, status_detail. The detail carries the reason;
the status only carries the category.
Payins: PENDING 100 · PAID 200 · REJECTED 300 · CANCELLED 400 · EXPIRED 600
Payouts: PENDING 100 · PAID 200 · REJECTED 300 · CANCELLED 400 · DELIVERED 500
DELIVERED is not final and not a failure — the money is in flight at the beneficiary's bank. It is
the status most often misread, so investigate payout calls it out explicitly.
Read the dLocal code, not the HTTP status — they disagree. A bad signature is 400 {"code":5000}
on payins and 403 {"code":"authentication_failed"} on payouts; there is no 401 on the payins
paths at all. 403 {"code":3001} is returned before the signature is checked, so it means the
caller was rejected outright — usually an IP allowlist or a wrong-host profile.
Clock skew is not a failure mode, despite X-Date being part of the signed message: the date is
signed and sent, so a drifted clock stays self-consistent and validates.
agent-dlocal mcp
Exposes the agent-facing groups as MCP tools. auth, config, and usage are deliberately not
exposed — credential management is an operator task, not something a tool-calling loop should reach.
make build # ./agent-dlocal
make test # unit + e2e
make lint
make build-mock # ./mockdlocal
make mock # serve on 127.0.0.1:12112
make mock-dev ARGS="payments get D-4-rejected"
mockdlocal --routes # the mocked surface
mockdlocal verifies the HMAC signature on every request, so the e2e suite is evidence that
signing works end to end rather than just that output parses.
See design-docs/initial-design.md for the endpoint inventory and the reasoning behind the auth
model, and design-docs/mock-dlocal.md for the mock's contract.
- Payouts v3 is not supported. It uses OAuth2 bearer tokens from
/oauth/tokenrather than signatures, which is a second credential model. v2 covers the same read surface today. - No
installments-plansretrieve command. Creating a plan is a mutation, and theGETretrieve-by-id form is not confirmed in dLocal's docs.api getcovers it if it exists.
PolyForm Perimeter 1.0.0 — see LICENSE.