Skip to main content
Six reads answer “how is this workspace doing”: your position now, the payments behind it, a bucketed series for a chart, your payout history, what is still to come, and what you owe. They are the numbers the Maat host app shows you, from the same place it reads them. They are not a copy of every screen in it.

Everything here is read-only

There is no payout request, no penalty settlement, no withdrawal method and no dispute on this API. Each of those moves real money or commits banking details, and both belong to a person signing in — not to a credential in a config file. There is no finance webhook either: a balance is a position you poll, not an event.

What you need

A key with the finance:read scope. Issuing one requires View Payouts and View Transactions in the workspace — the strictest issuance ceiling on this API, because these are the workspace’s own books.

An amount is always one object

Every figure is a Money — a decimal string and its currency, together:
Parse value with a decimal type, not a float. 1250.10 has no exact IEEE-754 representation, and a parser that rounds it costs a cent on every booking.

Currencies are never added together

A workspace has as many balances as it has settled currencies, and they are separate positions. GET /v1/finance/balance returns one partition per currency. A monthly summary returns one bucket per period and currency — a September in which you settled in both EGP and USD is two buckets, not one sum. Nothing in this API converts between currencies, and there is no total across them anywhere. If you want one headline number, convert the partitions yourself, with a rate you chose and can explain. A currency you have never traded in is absent, not zero. Absence means nothing has ever happened there.

earned_on is the check-in date

An earning line is dated by the stay’s check-in date, not by the day the money moved. June revenue means the stays that happened in June. It is the only bucketing that makes a year-over-year comparison line up seasonally, and it is the convention the Maat host dashboard uses — so the API and the app agree about the same month. A guest who books in March for a June stay appears in June.

One line is one payment, not one booking

GET /v1/finance/earnings returns a row per payment. A booking that is later modified takes its own additional charge, so it has a second row, with its own stay date. gross on a line is what the platform booked as your income for that payment — the payout plus any cancellation-insurance premium. It is already net of the platform’s commission and service fee, so it is not the guest’s total. For what the guest paid, read amount_paid on the booking. refunded is what was reversed, as a positive amount, and net is gross - refunded. platform_fee is Maat’s commission on that payment — the cut taken out of your rate, which is why gross is smaller than what the stay was priced at. It is the commission and nothing else: the guest’s service fee is charged on top of your rate rather than deducted from it, and card processing is ours to carry. It is published as a pair, like gross and refunded beside it. platform_fee is before any reversal of it; refunded_platform_fee is how much came back when the payment was reversed. What Maat kept is the difference. There is deliberately no single net-of-reversal figure: it would be the one number on the line measured on a different basis from its neighbours, so gross + platform_fee on a partly-refunded payment would add a pre-reversal amount to a post-reversal one and quietly come out wrong. Both figures are recorded when the payment is booked, on the same row and in the same database transaction as the money itself. Neither is reconstructed at read time. status says how far that payment’s money has got: reserved is the one that is easy to miss, and leaving it out is why a balance would not add up: available + reserved_for_payout + paid_out_total is everything that has cleared.

What you owe is not negative revenue

outstanding_penalties on a partition, and every row from GET /v1/finance/penalties, is a liability. It is never subtracted from your earnings anywhere in this API. A penalty is settled from released funds as stays check in, from your balance, or by card in the Maat app. written_off is a separate answer from settled on purpose: nothing was paid, somebody decided it would not be.

Windows and paging

from and to are inclusive at both ends and run over earned_on. A from after a to, or a window wider than 731 days, is refused with 400 validation_failed — never truncated, because a caller who believes they asked for five years and received two would reconcile against a total that is missing three. The summary requires from, to and granularity: it totals everything inside its window in one pass, so an unbounded one would sweep your whole history on every call. Lists page with page and page_size and answer has_more — there is no total. GET /v1/finance/payouts/upcoming is deliberately not paged: it is bounded by the platform (future release dates on confirmed stays only), not by your history.

In the sandbox

A sandbox workspace answers all six for real, and every answer is empty. A sandbox never reaches a payment gateway — no card is ever charged there — so no money has moved, no payout has been requested and nothing is owed. You get { "partitions": [] }, empty pages and an empty series. That is deliberate rather than a gap. The shapes are the production shapes, so you can build and type your integration against them; what you cannot do is exercise a number. Read the balance of a real workspace in the Maat app while you develop, and treat the sandbox as the contract check. Against a production workspace every one of the six returns 501 not_enabled today.

What is deliberately not here

Try it