Skip to main content
GET /v2/merchant/billing/invoices returns the authenticated merchant’s billing history for the trailing 365 days: per-invoice rows plus a totals envelope (paid amount, platform fees retained, invoice count). This endpoint is the data source for the merchant Billing History page in the dashboard.
This endpoint requires:
  • Merchant JWT — any authenticated merchant role (OWNER, MEMBER, PRODUCER)
The merchant scope is derived from the JWT sub claim. There is no merchantId query parameter. If a client passes one anyway it is silently ignored — the response is always scoped to the JWT-bound merchant.

GET /v2/merchant/billing/invoices

Authentication

GuardRequirement
JWTRequired, any authenticated merchant role
ScopemerchantId derived from JWT subnot from query / body
Obtain a merchant JWT via POST /merchant/admin/login — see Authentication. SUPER_ADMIN tokens work too (they are merchant JWTs with an extra role); the response is scoped to the SUPER_ADMIN’s own merchant record, not to the platform.

Query parameters

None. The window is fixed at trailing 365 days.

Example

curl "https://apiv3.droplinked.com/v2/merchant/billing/invoices" \
  -H "Authorization: Bearer <MERCHANT_JWT>"

Response — 200 OK

{
  "windowStart": "2025-06-14T23:08:50.155Z",
  "windowEnd": "2026-06-14T23:08:50.155Z",
  "totals": {
    "paidUsd": 0,
    "platformFeesRetainedUsd": 0,
    "invoiceCount": 0
  },
  "invoices": []
}

Fields

FieldTypeNullableDescription
windowStartISO-8601 stringNoStart of the rolling window (windowEnd − 365 days, server clock, UTC).
windowEndISO-8601 stringNoEnd of the rolling window (request timestamp, server clock, UTC).
totalsobjectNoWindow-aggregate envelope.
totals.paidUsdnumberNoSum of paid invoice amounts in the window. USD major units (dollars, not cents).
totals.platformFeesRetainedUsdnumberNoSum of platform-fee portion retained by Droplinked across the window. USD major units.
totals.invoiceCountintegerNoCount of invoices[] returned in the window.
invoicesarrayNoOne entry per paid invoice in the window, newest first. Empty array for merchants with no paid invoices in the trailing 365d.
invoices[].invoiceIdstringNoInvoice store _id.
invoices[].issuedAtISO-8601 stringNoInvoice issuance timestamp (UTC).
invoices[].paidAtISO-8601 stringYesPayment-settled timestamp. null for unpaid rows (only PAID invoices are returned today).
invoices[].paidUsdnumberNoInvoice amount paid, USD major units.
invoices[].platformFeeUsdnumberNoPlatform-fee portion of paidUsd retained by Droplinked.
invoices[].planIdstringYesSubscription plan id at the time of the invoice. null for one-off charges.
invoices[].invoicePdfUrlstringYesSigned CloudFront URL for the rendered PDF. null for legacy rows that pre-date PDF rendering.

Errors

StatusBodyWhen
401{ "statusCode": 401, "status": "failed", "data": { "message": "Unauthorized" } }Missing or invalid JWT
5xx{ "statusCode": 500, "status": "failed", ... }Hard backend failure — see Notes below for the fail-open contract

Notes

  • Trailing 365-day window. windowStart is computed as now − 365d on every call. The window is rolling, not anchored — two calls a day apart return slightly different windowStart / windowEnd values, and an invoice issued exactly 365d ago may drop out of one call’s window and back into the next.
  • Auth scope. The endpoint reads merchantId from the JWT sub claim only. Any ?merchantId= (or body merchantId) is silently ignored. A merchant cannot read another merchant’s invoices through this endpoint — even with a syntactically valid query parameter pointing at the target.
  • Fail-open semantics. If the underlying invoice store throws, the endpoint returns the empty-state envelope (invoices: [], totals.*: 0) with 200 OK rather than propagating the error. Inspect server logs / Sentry for the underlying failure. Merchants never see a partial billing-history page error in the dashboard.
  • Currency. All monetary fields are USD major units (dollars), not cents. Renders straight into Intl.NumberFormat({ style: 'currency', currency: 'USD' }) without dividing by 100. This contract diverges from the admin x402-earnings rollup (USD cents, integer) intentionally — merchant-facing endpoints serve pre-formatted human values; admin rollups serve aggregation-safe integer cents.
  • Only PAID invoices. Today the endpoint returns rows where status === 'PAID'. Pending / void / refunded invoices are out of scope for v1 and tracked for a future revision.