ISO360 API
A REST API for ISO360 agent offices. Push a merchant statement and get a rate analysis back as a proposal in your office; read your boarding applications. One bearer key per office, JSON in and out, over HTTPS.
Introduction
Every ISO360 API call is authenticated with a bearer key that belongs to a single agent office. The key carries the office identity, so you never pass an office or agent id in a request — whatever you create lands under the office that owns the key.
- Base URL:
https://www.cygma.cloud/api/v1 - Auth:
Authorization: Bearer <key>on every request. - Format: JSON responses; uploads use
multipart/form-data.
Authentication
Generate your key in ISO360 under My Office → ISO360 API. The full secret is shown once at generation — store it somewhere safe; it can’t be retrieved again. Regenerating immediately invalidates the previous key.
- Send it as
Authorization: Bearer <key>. Treat it like a password — server-side only, never in client code. - Each key is scoped (e.g.
proposals:create) and rate-limited per minute. Exceeding the limit returns429. - Lost or leaked a key? Regenerate it — the old one stops working instantly.
# Every request carries your office key as a bearer token. curl https://www.cygma.cloud/api/v1/applications \ -H "Authorization: Bearer epi_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Quickstart
- Create your office key in My Office → ISO360 API.
- POST a statement PDF (plus the merchant name) to Analyze a statement. The response carries the parsed numbers and a link to the proposal it created under your office.
# 1. Grab your office key from My Office → ISO360 API. # 2. Push a statement and read the analysis back. curl -X POST https://www.cygma.cloud/api/v1/proposals/analyze-statement \ -H "Authorization: Bearer <your-key>" \ -F "statement=@statement.pdf;type=application/pdf" \ -F "merchant_name=Jane's Coffee LLC" \ -F "mcc=5812"
Analyze a statement
Uploads a merchant’s prior-processing statement and returns a rate analysis. The call also creates a lead and a draft proposalin the office that owns the key, so the analysis is waiting in ISO360 when your agent logs in.
Request — multipart/form-data:
| Field | Required | Notes |
|---|---|---|
| statement | yes | PDF file, 25 MB max. |
| merchant_name | yes | Business name. |
| mcc | no | 4-digit MCC. Inferred when omitted. |
| address, city, state, zip | no | Physical address. Blanks are backfilled from the statement. |
| dba_name, legal_name, phone, email | no | Contact details for the lead. |
Pass an Idempotency-Key header to make retries safe: the same key returns the proposal created the first time instead of making a duplicate.
The call is synchronous and can take 15–30 seconds while the statement is read. If the statement can’t be parsed you get a 422(the draft proposal is still created so the agent can retry in-app).
curl -X POST https://www.cygma.cloud/api/v1/proposals/analyze-statement \ -H "Authorization: Bearer <your-key>" \ -H "Idempotency-Key: 7f3c1b9e-…" \ -F "statement=@statement.pdf;type=application/pdf" \ -F "merchant_name=Jane's Coffee LLC" \ -F "mcc=5812" \ -F "address=123 Example St" \ -F "city=Anytown" -F "state=ID" -F "zip=83702" \ -F "email=owner@example.com"
List applications
A paginated, read-only list of boarding applications. Requires the applications:read scope.
Query parameters:
page— 1-based page number (default 1).page_size— items per page, max 100 (default 25).status— filter by application status.merchant_id— filter to one merchant.
The response envelope (data, page, page_size, total, has_more) is the same across every list endpoint.
curl "https://www.cygma.cloud/api/v1/applications?page=1&page_size=25&status=SUBMITTED" \ -H "Authorization: Bearer <your-key>"
Routing-number lookup
Resolve a 9-digit ABA routing number to its financial institution. Backed by EPI’s internal mirror of the Federal Reserve FedACH Participants Directory (refreshed monthly) — the same authoritative source the ISO360 and Merchant360 apps use, with no third-party dependency. Requires the banking:read scope.
Path parameter:
routing— the 9-digit ABA routing number (non-digits are stripped).
Responses: 422 if the number isn’t 9 digits or fails the ABA Mod-10 checksum; 404 if it passes the checksum but isn’t in the directory (a newer/smaller institution).
GET /api/v1/banking/routing/121000248
This is a fixed sample. The live GET /api/v1/banking/routing/{routing} looks up any routing number — add Authorization: Bearer <your-key> and call it from your server.
curl "https://www.cygma.cloud/api/v1/banking/routing/121000248" \ -H "Authorization: Bearer <your-key>"
Card BIN lookup
Identify a card from its leading digits. Alongside brand, credit / debit / prepaid, issuing bank, country, and the surcharge verdict, the response decodes the full card profile: the card indicator (e.g. H → “Debit hybrid — PIN and signature”), PIN / signature acceptance, the Durbin regulated-issuer flag, and the named debit networks the card routes on (with PINless capability). A credit card that also carries debit networks is flagged dual_routing. The product_id is decoded to a human-readable product tier in product_id_label (e.g. D → “Visa Signature Preferred”). Requires the banking:read scope.
Test data only. This endpoint serves a small static sample set — the well-known test BINs (411111, 555555, 371449, 601100, a debit, a prepaid, and a regulated example) — so you can build and exercise every response shape. It does not serve EPI’s production BIN directory; production lookups are available by agreement with Electronic Payments.
Path parameter:
bin— the first 6–16 digits of the card number (non-digits are stripped).
Responses: 200 with the card attributes; 422 if the BIN isn’t 6–16 digits; 404 for any BIN outside the sample set (the body lists the available samples).
GET /api/v1/banking/bin/411111
These are fixed test BINs. The live endpoint serves the same sample set only; production BIN lookups are available by agreement with Electronic Payments.
curl "https://www.cygma.cloud/api/v1/banking/bin/486208" \ -H "Authorization: Bearer <your-key>"
Responses
Successful responses return JSON with the resource under a data key. List endpoints add page, page_size, total, and has_more for pagination. The status code tells you what happened:
| Status | Meaning |
|---|---|
| 200 | OK — a read, lookup, or list succeeded; the resource is under data. |
| 201 | Created — a new resource (e.g. a proposal from an analyzed statement) was created. |
| 202 | Accepted — the request was queued for asynchronous processing. |
// Reads + single-object responses wrap the object in `data`.
{ "data": { "id": "...", "...": "..." } }
// List endpoints wrap an array in `data` with pagination.
{ "data": [ { "...": "..." } ], "page": 1, "page_size": 25, "total": 42, "has_more": true }Errors
| Status | Meaning |
|---|---|
| 401 | Missing, malformed, invalid, or revoked key. |
| 403 | Key lacks the required scope (or isn’t office-bound). |
| 413 | Statement file too large (25 MB max). |
| 415 | Body isn’t multipart, or the file isn’t a PDF. |
| 422 | Missing a required field, or the statement couldn’t be parsed. |
| 429 | Rate limit exceeded — back off and retry. |
// Errors return a JSON body with a single `error` string.
{ "error": "Missing required scope: proposals:create" }Versioning
The API is versioned in the path (/api/v1). Within a version we only make backward-compatible changes: new fields are always optional, and we never remove or repurpose an existing field. A breaking change ships under a new version, and the old one keeps running through a deprecation window.
Build a tolerant client: ignore response fields you don’t recognize, and don’t depend on field order. That way new additions never break your integration.