> This page is for Partner API.

> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.getmodulate.dev/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.getmodulate.dev/_mcp/server.

# Quickstart

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

### 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](/partner-api/overview/getting-started/authentication) to request one from the Aurora Payments team.

### Check connectivity

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

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

A healthy response looks like:

```json
{
  "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": []
}
```

### List your merchants

Pull the first page of merchants in your portfolio:

#### cURL

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

#### Python

```python
import httpx

client = httpx.Client(
    base_url="https://getmodulate.dev",
    headers={"X-Partner-API-Key": "YOUR_API_KEY"},
)

response = client.get("/api/partner/merchants", params={"page_size": 25})
body = response.json()

for merchant in body["data"]:
    print(merchant["merchant_id"], merchant["dba_name"], merchant["status"])
```

#### TypeScript

```typescript
const response = await fetch(
  "https://getmodulate.dev/api/partner/merchants?page_size=25",
  { headers: { "X-Partner-API-Key": "YOUR_API_KEY" } }
);

const { data, meta } = await response.json();
console.log(`Found ${meta.total_count} merchants`);

data.forEach((m: any) => console.log(m.merchant_id, m.dba_name, m.status));
```

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

```json
{
  "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": []
}
```

### Get current pricing for a merchant

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

#### cURL

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

#### Python

```python
mid = "925700000233734"
response = client.get(f"/api/partner/merchants/{mid}/pricing/tsys/current")
pricing = response.json()["data"]

summary = pricing["summary"]
print(f"Model: {summary['pricing_model']}")
print(f"Rate: {summary['effective_rate_bps']} bps")
print(f"Per-txn: {summary['per_transaction_fee']}")
```

#### TypeScript

```typescript
const mid = "925700000233734";
const res = await fetch(
  `https://getmodulate.dev/api/partner/merchants/${mid}/pricing/tsys/current`,
  { headers: { "X-Partner-API-Key": "YOUR_API_KEY" } }
);

const { data: pricing } = await res.json();
console.log(`Model: ${pricing.summary.pricing_model}`);
console.log(`Rate: ${pricing.summary.effective_rate_bps} bps`);
```

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

```json
{
  "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": []
}
```

### Explore more endpoints

You're connected. Here's what to explore next:

#### [Residuals](/partner-api/api-reference/api-reference/residuals/list-available-reports-api-partner-residuals-reports-get)

Pull payout summaries, per-merchant residuals, adjustments, and withholds for any reporting period, or an AI brief that explains a month.

#### [Statements](/partner-api/api-reference/api-reference/statements/list-all-statements-api-partner-statements-get)

List and download merchant statements across your entire portfolio.

#### [Transactions](/partner-api/api-reference/api-reference/transactions/get-daily-transactions-api-partner-merchants-merchant-id-transactions-daily-get)

Access daily transaction summaries and detailed transaction history.

#### [PCI Compliance](/partner-api/api-reference/api-reference/compliance/get-compliance-status-api-partner-merchants-merchant-id-compliance-get)

Check SAQ status, scan results, and compliance history for any merchant.

#### [Processor Profiles](/partner-api/api-reference/api-reference/processor-profiles/get-tsys-processor-profile-api-partner-merchants-merchant-id-processor-profile-tsys-get)

Pull the full TSYS or Omaha processor profile — card networks, banking, hierarchy, interchange qualification, and 7 more sections.

## Response envelope

Every endpoint returns the same envelope structure:

```json
{
  "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:

```bash
# 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:

```python
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:

```bash
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.

> **Info**
>
> 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`:

```json
{
  "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:

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

## Need help?

Contact [it@flute.com](mailto:it@flute.com) or reach out to your account manager.