Skip to content

Your own systems hear about your money the moment it moves

Point an address of yours at a space. Orla calls it with a signed JSON body when money lands or leaves, an invoice is paid, a payout goes out, a payment waits for a signature or is stopped.

  1. Orla sendsX-Orla-Event: money.in

    POST /your/address HTTP/1.1
    Content-Type: application/json
    X-Orla-Event: money.in
    X-Orla-Event-Id: 11b8c41e-47b5-407b-a19d-bb12d90a82b5
    X-Orla-Delivery-Attempt: 1
    X-Orla-Timestamp: 1789748060
    X-Orla-Signature: t=1789748060,v1=5c8456449f0e...
    
    {"event":"money.in","version":1,
     "transaction_id":"5c5cbbda-f6a7-4907-9c10-e167a238b6e2",
     "account_id":"3a7b76ee-7acd-414e-9066-af408c2b74b7",
     "direction":"in","amount":"1.00000000","currency":"USD",
     "kind":"income","origin":"manual","occurred_on":"2026-09-18",
     "at":"2026-09-18T13:14:20.312110+00:00"}
  2. You answerAny 2xx within ten seconds

    HTTP/1.1 200 OK

In the app: Connections, the card Event subscriptions. An owner or an admin adds the address and picks the events.

Six events and a ping

One name on the wire,
the same name on the screen

Pick the ones you want, or pick none and hear about all of them, including events added later. A move between your own accounts arrives with origin internal, so an automation that ships goods on payment is not fired by your own sweep. What the autopilot pays reaches you as payout.executed. What it holds for a person reaches you as payment.awaiting_approval.

Deliveries
  • money.in13:14 · attempt 1200
  • payout.executed13:02 · attempt 1200
  • payment.awaiting_approval12:47 · attempt 1200
  • invoice.paid12:31 · attempt 2 · next at 12:36503
  • ping09:00 · attempt 1200
Retry
money.in
A row landed in the book that increases an account.transaction_id, account_id, direction (in), amount, currency, kind (income, expense, transfer), origin (rail, manual, import, internal), occurred_on
money.out
A row landed in the book that decreases an account.The same as money.in, with direction out
invoice.paid
An invoice the space issued is settled in full. Fires once per invoice.invoice_id, number (your document number), amount, currency
payout.executed
A payment left on a rail and the rail confirmed it.payment_id, amount, currency, rail (a chain name; bank, for any payout provider; or book for a payment marked paid by hand), transaction_id
payment.awaiting_approval
A payment met an approval rule and waits for signatures.payment_id, amount, currency, required_approvals
payment.blocked
An automatic run stopped a payment before it left, because a rule it matched was switched off.payment_id, amount, currency, reason (a stable error code, not a sentence)
ping
Once, when you save the address and when you rotate the secret. Not in the picker: every subscription gets it, and the address is only saved if it answers 2xx.None beyond the common ones

The body

Amounts and identifiers.
Never names

Every body carries event, version and at, then its own fields. Amounts are unsigned decimal strings, and no event carries a name, a note or an address.

Common fields

  • eventThe event name, the same string as the X-Orla-Event header.
  • versionThe body version, 1 today. Within a version a field is only ever added, never renamed or removed.
  • atWhen the event was written, ISO 8601 with an offset.
  • amountA decimal string with eight places, never a float, never signed.

The signature

Compute it on the bytes you received,
before you parse them

X-Orla-Signature carries t=<unix seconds>,v1=<hex>: the HMAC-SHA256, under the secret shown once, of the timestamp, a dot and the raw body. Refuse a timestamp older than five minutes, compare in constant time, and drop a repeat by X-Orla-Event-Id.

import hashlib, hmac, time

def verify(secret: str, body: bytes, header: str) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    stamp, given = parts["t"], parts["v1"]
    if abs(time.time() - int(stamp)) > 300:
        return False
    signed = f"{stamp}.".encode() + body
    want = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    return hmac.compare_digest(given, want)

body is the raw bytes of the request, not an object you serialised again: a JSON library that reorders keys or changes spacing changes the digest. The window is yours to choose; five minutes covers clock drift without covering a replay.

Delivery

At least once,
within a minute, for about a day

send1 min5 min15 min1 h3 h6 h12 ha day

What to answer

  • Any 2xx within ten secondsThat counts as delivered. Do the work after you answer, not before.
  • Anything elseA retry: after 1 minute, 5, 15, then 1 hour, 3, 6 and 12. Eight attempts over about a day, then the delivery is marked as given up in the journal.
  • A day of nothing but failuresThe subscription is paused and the owner of the space is told. Fix the address and press Resume in the same place.

What you can see

  • The journalEvery delivery for thirty days: event, status, attempt, response code, and a Retry button on one row.
  • TwinsA receiver that answers slowly may be sent the same event twice. Drop the second by X-Orla-Event-Id.

Setting it up

An address,
a choice of events, a secret you copy once

The address

  • Public https on port 443

    Resolved again at every send. A private range, a loopback or a metadata address is refused, whatever the hostname says that day.

  • Redirects are not followed

    Answer at the address you gave.

  • A ping before it is saved

    Saving sends a signed ping event. An address that does not answer 2xx within ten seconds is not saved, and the form says so; the same happens when you rotate the secret, so the old one keeps working until the receiver has proven it holds the new one.

Who and how many

  • Owner or adminA subscription carries everything that happens in the space and does not filter by role, so the people who may add one are the people who already see it all.
  • PlansPro carries one subscription across the spaces of the plan, Scale five, Enterprise twenty five, and a pack adds five more. Free and Solo do not include it.
  • The secretShown once, at creation and at rotation. Orla keeps only an encrypted copy, so New secret is the only answer to a lost one.

Write your first mandate
on a thirty minute call

On your own books: one position, one rule, and the first bill waiting for your button.