Skip to navigation

Quickstart

Make your first API call and pull live merchant data in under 5 minutes

This guide walks you through authenticating, listing your merchants, and pulling pricing data — the three calls you’ll make in almost every integration.

1

Get your API key

You’ll need an X-Partner-API-Key to authenticate. If you don’t have one yet, see the Authentication guide to request one from the Aurora Payments team.

2

Check connectivity

Verify the API is reachable with a health check (no auth required):

curl https://getmodulate.dev/api/partner/health

A healthy response looks like:

{
"data": {
"status": "ok",
"version": "v1",
"database": "connected",
"build": "partner-api-v202608201200"
},
"meta": {
"request_id": "req_abc123",
"api_version": "v1",
"timestamp": "2025-01-15T10:30:00Z"
},
"errors": []
}
3

List your merchants

Pull the first page of merchants in your portfolio:

curl https://getmodulate.dev/api/partner/merchants?page_size=25 \
-H "X-Partner-API-Key: YOUR_API_KEY"

Every response uses the {data, meta, errors} envelope. List endpoints include pagination details in meta:

{
"data": [
{
"merchant_id": "925700000233734",
"dba_name": "Sunrise Coffee Co",
"legal_name": "Sunrise Coffee LLC",
"status": "active",
"processor": "TSYS",
"mcc_code": "5812",
"state": "CA",
"city": "San Francisco",
"onboarding_date": "2024-03-15T00:00:00+00:00",
"last_data_refresh": "2025-01-15T10:30:00+00:00",
"pci_compliance": {
"status": "compliant",
"saq_due_date": "2025-12-31T00:00:00+00:00",
"last_updated": "2025-01-10T00:00:00+00:00"
}
}
],
"meta": {
"request_id": "req_abc123",
"api_version": "v1",
"page": 1,
"page_size": 25,
"total_count": 142,
"total_pages": 6,
"has_next": true,
"has_prev": false,
"source": "risecrm",
"as_of": "2025-01-15T10:30:00+00:00"
},
"errors": []
}
4

Get current pricing for a merchant

Use a merchant_id from the previous step to fetch their current TSYS pricing:

curl https://getmodulate.dev/api/partner/merchants/925700000233734/pricing/tsys/current \
-H "X-Partner-API-Key: YOUR_API_KEY"

The pricing response includes a computed summary and the full fee breakdown by category:

{
"data": {
"merchant_id": "925700000233734",
"pricing_model": "IC+",
"summary": {
"pricing_model": "IC+",
"pricing_text": "IC+ 15 bps + $0.10",
"primary_rate_bps": 15,
"other_volume_fee_bps": 10,
"effective_rate_bps": 25,
"per_transaction_fee": "0.10",
"monthly_fee": "9.95",
"annual_fee": null,
"exceptions": [
{ "card_type": "Amex", "rate": 2.95 }
]
},
"categories": [
{
"name": "Discount",
"items": [
{
"fee_item_name": "Discount Rate",
"percent": 0.15,
"per_item": null,
"billing_frequency": "Monthly"
}
]
}
],
"as_of": "2025-01-01T00:00:00+00:00"
},
"meta": {
"request_id": "req_def456",
"api_version": "v1",
"source": "bigquery",
"as_of": "2025-01-01T00:00:00+00:00"
},
"errors": []
}

Response envelope

Every endpoint returns the same envelope structure:

{
"data": { }, // Single object or array
"meta": { }, // Request ID, pagination, data source, freshness timestamp
"errors": [] // Empty on success; error details on failure
}

List endpoints add pagination fields to meta: page, page_size, total_count, total_pages, has_next, has_prev.

Pagination

All list endpoints support page and page_size query parameters:

# Get page 3 with 50 results per page
curl "https://getmodulate.dev/api/partner/merchants?page=3&page_size=50" \
-H "X-Partner-API-Key: YOUR_API_KEY"

Use has_next from the response meta to drive pagination loops:

page = 1
while True:
response = client.get("/api/partner/merchants", params={"page": page, "page_size": 100})
body = response.json()
for merchant in body["data"]:
process(merchant)
if not body["meta"]["has_next"]:
break
page += 1

Incremental sync

Use updated_since on supported endpoints to fetch only records that changed since your last sync:

curl "https://getmodulate.dev/api/partner/merchants?updated_since=2025-02-01T00:00:00Z" \
-H "X-Partner-API-Key: YOUR_API_KEY"

Pass an ISO-8601 timestamp. A value without an offset is treated as UTC. URL-encode the + in offsets such as +05:00 (%2B05:00), or it arrives as a space and returns 422.

Supported on: merchants list, merchant residuals, and available reports.

On the merchants list, updated_since matches merchants approved or closed at or after the timestamp. Other field edits (name, address, assigned agent) do not count as updates, so pair incremental syncs with a periodic full sync.

Error handling

Failed requests (any non-2xx status) return the same envelope with data: null and one or more items in errors:

{
"data": null,
"meta": { "request_id": "req_xyz789", "api_version": "v1" },
"errors": [
{
"code": "MERCHANT_NOT_IN_PORTFOLIO",
"message": "Merchant not found or not in your portfolio",
"field": null,
"retryable": false
}
]
}

Every error item has code, message, field (the offending parameter on 422, otherwise null) and retryable, which is always a JSON boolean.

  • Successful (2xx) responses always carry an empty errors array. A 200 is never a partial failure: an empty data list means nothing matched.
  • When a response is complete but a secondary source was unavailable, the gap is flagged in meta.warnings, not errors. For example, the merchants list returns every row with pci_compliance: null and a warning if compliance data could not be loaded.
  • retryable: true (429, 500, 503) means the same request may succeed later. For 429, wait for Retry-After. retryable: false (401, 403, 404, 422) means the request must change before it can succeed.
  • Quote meta.request_id when contacting support.

Common status codes:

StatusMeaning
401Missing or invalid API key
403Merchant not in your portfolio or insufficient scope
404Resource not found
429Rate limit exceeded — check Retry-After header
503Upstream data source temporarily unavailable

Need help?

Contact it@flute.com or reach out to your account manager.