Data API
If you don't use a gateway we integrate with, send the data straight here. You can also type it into the screen or upload a spreadsheet: same source, same rules. Not a single line of code is required.
In this source, the data comes from you. There is no gateway on the other side to check anything, so what you declare here is the truth every number in your account is computed from. If your system stops sending, your dashboard freezes at the last thing you declared, and we tell you.
Getting started
- In Metricaas, add a Custom data source and answer the three questions (whether you'll enter payments, whether each subscription has a plan, whether you'll register customers).
- Under Settings > API, generate a key. It is shown once.
- Send your subscriptions. The dashboard moves within a minute.
Authentication
Every call carries your key in the header. The key belongs to your company and says which source it writes to.
Authorization: Bearer mtc_live_...
Base address: https://api.metricaas.ai/v1We only store a cryptographic digest of the key, so not even we can show it again. Lose it, generate another and revoke the old one.
Your first call
Create and update are the same call: the id is yours, and sending the same id again updates instead of duplicating.
curl -X POST https://api.metricaas.ai/v1/subscriptions \
-H "Authorization: Bearer mtc_live_..." \
-H "Content-Type: application/json" \
-d '{"id":"sub_001","customer":"cli_001","plan":"Pro","currency":"BRL","cycle":"monthly","started_at":"2024-01-10","amounts":[{"from":"2024-01-10","cents":9990},{"from":"2025-03-01","cents":12990}]}'Send in BATCH (an array in the body, up to 1000 objects). Either everything goes in or nothing does: a half-applied batch would leave you not knowing what landed.
An amount is a HISTORY, not a number
In a gateway, price history lives in the invoices: each one is a billed period, at that day's price. Here there are no invoices, so the history lives inside the subscription, in the `amounts` field.
Each band says what it became worth, and from when. The first one starts when the subscription started. That is what keeps March's MRR being March's after you raise the price in April.
{
"amounts": [
{
"from": "2024-01-10",
"cents": 9990
},
{
"from": "2025-03-01",
"cents": 12990
}
]
}So a request that SHORTENS or ALTERS an already declared band is rejected with 409: it would rewrite the MRR of months that already closed. If it was a price change, append a new band. If it was a typo, confirm you really want to rewrite:
POST https://api.metricaas.ai/v1/subscriptions?correct_history=trueCanceling is not deleting
Canceling is a fact about your business: the customer left, the subscription stays in your history and becomes churn on the declared date. Deleting is an erratum: it never existed, and past MRR is recomputed without it. Confusing the two corrupts your churn in both directions.
# the customer left (becomes churn)
PUT https://api.metricaas.ai/v1/subscriptions/sub_001 { "canceled_at": "2026-03-01", ... }
# I typed it wrong (erased from history)
DELETE https://api.metricaas.ai/v1/subscriptions/sub_001/v1/customers
Optional. Only needed if one customer has more than one subscription. The only required field is the id, so you can group without telling us who they are.
| Field | Type | Required | What it is |
|---|---|---|---|
id | string | The identifier YOU choose. Sending the same id again updates the customer, it doesn't duplicate. | |
name | string | Customer name. Optional: you never have to tell us who they are. | |
email | string | Customer email. | |
created_at | date | When they became your customer. |
{
"id": "cli_001",
"name": "Empresa Exemplo",
"email": "contato@exemplo.com"
}/v1/subscriptions
The heart of the source. This is where your MRR comes from.
| Field | Type | Required | What it is |
|---|---|---|---|
id | string | The identifier YOU choose. Sending the same id again updates the subscription, it doesn't duplicate. | |
customer | string | The id of the customer who owns this subscription. Leave it blank and the subscription counts as one customer (you don't have to register customers at all). | |
plan | string | The name of the product it subscribes to. Only has an effect if your source declares plans. | |
currency | enum(BRL | USD | EUR | GBP | ...) | This subscription's currency, ISO-4217. An unknown currency is rejected, never assumed. | |
cycle | enum(daily | weekly | biweekly | monthly | bimonthly | quarterly | semiannually | yearly) | How often you charge for it. | |
started_at | date | When it started. The first band in `amounts` must start exactly here. | |
canceled_at | date | When it was canceled. This is the churn date, and it is declared: there is no gateway here to tell us. | |
trial_end | date | When the free trial ended. The period up to this date never becomes MRR. | |
amounts | array<{ from: date, cents: integer }> | The HISTORY of the amount, dated. It isn't an amount: it's the list of what it was worth, and since when. Read the section above before touching this. |
{
"id": "sub_001",
"customer": "cli_001",
"plan": "Pro",
"currency": "BRL",
"cycle": "monthly",
"started_at": "2024-01-10",
"amounts": [
{
"from": "2024-01-10",
"cents": 9990
},
{
"from": "2025-03-01",
"cents": 12990
}
]
}/v1/payments
Optional. Declare payments and you get dunning, cash revenue, fees and refunds. Don't, and MRR stays whole while dunning simply doesn't exist in your account.
| Field | Type | Required | What it is |
|---|---|---|---|
id | string | The identifier YOU choose. | |
customer | string | The id of whoever paid. Either this or `subscription`: a payment always says whose it is. | |
subscription | string | The id of the subscription this payment settles. Blank means a one-off sale (no MRR; it counts as revenue). | |
amount_cents | integer | The gross amount paid, in cents. | |
paid_at | date | When the money came in. | |
fee_cents | integer | The payment processing fee, in cents. | |
refunded_cents | integer | How much went back to the customer, in cents. Partial refunds are valid. | |
currency | enum | If left blank, we use the subscription's (or your account's, for a one-off). |
{
"id": "pay_001",
"customer": "cli_001",
"subscription": "sub_001",
"amount_cents": 9990,
"paid_at": "2024-02-10",
"fee_cents": 349
}Errors
A rejection lists ALL the problems at once, with the field (and, for spreadsheets, the row). You won't be fixing one per request.
{
"error": {
"code": "invalid_request",
"message": "a declaracao tem 2 problema(s)",
"details": [
{
"field": "subscription.cycle (sub_002)",
"line": 4,
"reason": "invalid option"
},
{
"field": "subscription.amounts (sub_007)",
"line": 9,
"reason": "..."
}
]
}
}202— accepted. Your dashboard moves within a minute.400— the declaration has problems. Nothing was written.401— invalid key, revoked, or without permission for this operation.409— this would rewrite already declared amount history. See the section above.429— too many requests. Send in batch.
Checking what you declared
GET returns what's in your vault, paginated by cursor. This same key will read your metrics once the read API exists.
GET https://api.metricaas.ai/v1/subscriptions?limit=100&cursor=sub_050