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.
Build against merchants, applications, proposals, residuals, statements, the equipment catalog, and the reference lists on a sandbox office with demo merchants. No office agreement needed.
Open the developer sandboxAuthentication
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.
No office yet? Grab a self-serve read-only key from the sandbox and build against demo merchants first.
# Every request carries your office key as a bearer token. curl https://www.cygma.cloud/api/v1/applications \ -H "Authorization: Bearer epi_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Developer sandbox
Request a key at www.cygma.cloud/iso360/sandbox. It is issued instantly and bound to a shared sandbox office (SANDBOX360) that owns three demo merchants, so every read returns a realistic shape without touching a real office’s book.
- Read-only. Granted scopes are
merchants:read,applications:read,proposals:read,residuals:read,statements:read,orders:read,reference:read, andwebinars:read. - No writes at all. Boarding, change requests, terminal provisioning, and orders need write scopes EPI grants to a real office. A sandbox key gets
403on those. - Capped. Five active keys per email address, and a global daily ceiling. Lost a key? Request another.
# Request a sandbox key (no auth). One call, key comes back once.
curl -X POST https://www.cygma.cloud/api/v1/sandbox/keys \
-H "Content-Type: application/json" \
-d '{ "email": "you@company.com", "company": "Acme Payments" }'
# Then use it like any office key.
curl https://www.cygma.cloud/api/v1/merchants \
-H "Authorization: Bearer <your-sandbox-key>"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 proposal in 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"
Proposals lifecycle
Build a pricing proposal end-to-end: open a draft (anchored to a merchant or a lead) → save a priced version from pricing knobs → generate the PDF → send it → mark accepted. Writes need proposals:write; reads need proposals:read. The acting user is the key’s creator, and every write reuses the same validation the in-app builder runs – including the office’s pricing limits on a version save.
Endpoints:
POST /proposals– open a draft (merchant_idorlead_id).GET /proposals·GET /proposals/{id}– list / fetch.POST /proposals/{id}/versions– save a priced version (knobs.pricing_modelrequired).POST /proposals/{id}/pdf– generate the merchant PDF;GETthe same path downloads it.POST /proposals/{id}/send– email it (to_email) and markSENT.POST /proposals/{id}/accept– markACCEPTED.
| Field | Type | Description |
|---|---|---|
merchant_idoptional | string | Anchor to an existing merchant. Provide this OR lead_id (exactly one). |
lead_idoptional | string | Anchor to a lead instead of a merchant. |
target_savings_pctoptional | number | Optional savings target to prompt the pricing engine. |
target_savings_dollarsoptional | number | Optional dollar savings target. |
| Field | Type | Description |
|---|---|---|
knobsrequired | object | Pricing knobs. At minimum pricing_model (IC_PLUS | FLAT | CASH_DISCOUNT | SURCHARGE) plus the rate fields (e.g. discount_rate_bps, per_auth_cents, monthly_fee_cents). Validated against your office’s pricing limits. |
current_processingoptional | object | Optional statement facts for the savings math: annual_volume, avg_ticket, effective_rate_pct, annual_fees, processor, card mix. |
modeoptional | string | create (default, new version) | update_latest (overwrite the latest). |
| Field | Type | Description |
|---|---|---|
to_emailrequired | string | Recipient for the merchant-facing PDF. Marks the proposal SENT. |
The response and the PDF carry the merchant-facing pricing figures.
# 1. open a draft against a merchant (or a lead)
curl -X POST "https://www.cygma.cloud/api/v1/proposals" \
-H "Authorization: Bearer <your-key>" -H "Content-Type: application/json" \
-d '{ "merchant_id": "<merchant-id>", "target_savings_pct": 15 }'
# 2. save a priced version
curl -X POST "https://www.cygma.cloud/api/v1/proposals/<id>/versions" \
-H "Authorization: Bearer <your-key>" -H "Content-Type: application/json" \
-d '{ "knobs": { "pricing_model": "IC_PLUS", "discount_rate_bps": 25, "per_auth_cents": 10 },
"current_processing": { "annual_volume": 1200000, "avg_ticket": 42, "effective_rate_pct": 3.1 } }'
# 3. generate the PDF, then 4. send it
curl -X POST "https://www.cygma.cloud/api/v1/proposals/<id>/pdf" -H "Authorization: Bearer <your-key>"
curl -X POST "https://www.cygma.cloud/api/v1/proposals/<id>/send" -H "Authorization: Bearer <your-key>" \
-H "Content-Type: application/json" -d '{ "to_email": "owner@merchant.com" }'Board a merchant
Board a merchant end-to-end. POST /applications (applications:write) opens a draft merchant + application from a structured body – business identity, owners (SSN/DOB/EIN are encrypted at rest), banking, risk, and an optional pricing template – attributed to your office. Target NORTH / OMAHA (Fiserv) or CYGMA.
“Required” below means required for this call to succeed. Fields marked (to submit) are optional at create but must be present before POST /applications/{id}/submit passes – send them now or via PATCH. Run /preflightany time to see exactly what’s still missing.
| Field | Type | Description |
|---|---|---|
businessrequired | object | Business identity block (see below). |
preferred_backendoptional | string | Processor to board on: NORTH (default), OMAHA, or CYGMA. |
ownersoptional | object[] | Beneficial owners / signers. (≥1 required to submit.) |
bankoptional | object | Deposit account. (Required to submit.) |
riskoptional | object | Processing volumes + sales mix (feeds underwriting). |
pricing_template_idoptional | string | Attach a rate card from GET /pricing-templates. The id is shown on each template in ISO360 (Pricing Templates) with a copy-to-clipboard API chip. |
equipment_template_idoptional | string | Attach a saved equipment bundle from GET /equipment-templates (materializes a draft order with the template lines). The id is shown on each template in ISO360 (Equipment Templates) with a copy-to-clipboard API chip. |
| Field | Type | Description |
|---|---|---|
dba_namerequired | string | Doing-business-as name. |
legal_namerequired | string | Legal entity name (W-9 line 1). |
addressrequired | string | Physical / DBA street address. |
cityrequired | string | DBA city. |
staterequired | string | 2-letter state. |
ziprequired | string | DBA ZIP. |
business_start_dateoptional | string | YYYY-MM-DD. (Required to submit.) |
mccoptional | string | 4-digit merchant category code. (Required to provision a Cygma TID.) Look one up via /reference/mcc. |
einoptional | string | Federal Tax ID / EIN. Encrypted at rest; only last-4 is ever returned. |
tin_typeoptional | string | SSN | EIN | ITIN. |
tax_classificationoptional | string | Entity type – see /reference/business-types. |
business_typeoptional | string | Free-text business description. |
phoneoptional | string | DBA phone. |
emailoptional | string | Merchant contact email (lowercased). |
websiteoptional | string | Merchant website. |
| Field | Type | Description |
|---|---|---|
full_namerequired | string | Owner's full legal name. |
ownership_pctoptional | number | Percent ownership. Owners ≥25% must have SSN, DOB, and full address to submit. |
ssnoptional | string | 9-digit SSN. Encrypted at rest. (Required to submit for owners ≥25% + control persons.) |
doboptional | string | YYYY-MM-DD. Encrypted at rest. (Required to submit, same rule.) |
address / city / state / zipoptional | string | Owner home address. (Required to submit, same rule.) |
is_signeroptional | boolean | Marks a signing owner. Signers must total ≥50% equity to submit (auto-promoted if short). |
is_control_onlyoptional | boolean | FinCEN control person with no equity – still needs SSN/DOB/address. |
titleoptional | string | Owner title (e.g. President). |
email / phoneoptional | string | Owner contact. |
| Field | Type | Description |
|---|---|---|
routingoptional | string | 9-digit ABA routing. (Required to submit.) Validate via /banking/routing. |
accountoptional | string | Deposit account number. (Required to submit.) |
account_typeoptional | string | CHECKING (default) | SAVINGS. |
bank_nameoptional | string | Bank name (recommended). |
| Field | Type | Description |
|---|---|---|
annual_volumeoptional | number | Annual card volume, dollars. |
average_ticketoptional | number | Average sale, dollars. |
high_ticketoptional | number | Highest single sale, dollars. |
pct_swiped / pct_keyed / pct_moto / pct_internetoptional | number | Entry mix %, should total 100. |
pct_retail / pct_b2b / pct_mobileoptional | number | Sales-location mix %. |
| Field | Type | Description |
|---|---|---|
signature_namerequired | string | The signer's typed name (the e-signature). |
signature_bloboptional | string | Optional signature image as a data URL. |
confirmation_signature_nameoptional | string | Defaults to signature_name. |
confirmation_signature_bloboptional | string | Defaults to signature_blob. |
POST /applications/{id}/preflighttells you exactly what still blocks a submit (the same completeness gate the app enforces), plus the backend’s own requirements – the Fiserv MPA missing[] for NORTH/OMAHA (most of which are completed downstream at File Build), or the MID + 4-digit MCC note for Cygma (which provisions its TID after approval).
POST /applications/{id}/submitexecutes the signed application and kicks off underwriting (auto due-diligence, GIACT, OFAC, risk scoring). Supply the signer’s name; a 422 returns the reason if the gate isn’t satisfied.
You don’t have to send everything at once – PATCH /applications/{id} fills a DRAFT iteratively (same body, only the fields you send change; owners replaces the list). Set the FinCEN control_question_answered flag here too. Editing is DRAFT-only.
Cygma go-live (MID assignment + TID provisioning) happens on EPI’s side after approval. Owner + bank data must be present before submit.
# 1. open a draft application (NORTH / OMAHA / CYGMA)
curl -X POST "https://www.cygma.cloud/api/v1/applications" \
-H "Authorization: Bearer <your-key>" -H "Content-Type: application/json" \
-d '{
"preferred_backend": "CYGMA",
"business": { "dba_name": "Joe\'s Diner", "legal_name": "Joe Diner LLC",
"address": "1 Main St", "city": "Austin", "state": "TX", "zip": "78701",
"mcc": "5812", "business_start_date": "2019-03-01", "ein": "12-3456789", "tin_type": "EIN" },
"owners": [ { "full_name": "Joe Smith", "ownership_pct": 100, "ssn": "123-45-6789",
"dob": "1980-01-01", "address": "1 Main St", "city": "Austin", "state": "TX", "zip": "78701",
"is_signer": true } ],
"bank": { "routing": "021000021", "account": "123456789", "account_type": "CHECKING" },
"risk": { "annual_volume": 1200000, "average_ticket": 42, "high_ticket": 500 }
}'
# 2. check readiness 3. execute (e-sign)
curl -X POST "https://www.cygma.cloud/api/v1/applications/<id>/preflight" -H "Authorization: Bearer <your-key>"
curl -X POST "https://www.cygma.cloud/api/v1/applications/<id>/submit" -H "Authorization: Bearer <your-key>" \
-H "Content-Type: application/json" -d '{ "signature_name": "Joe Smith" }'Account servicing
Submit post-boarding changes on a live merchant. All three use changes:write and follow the same rule: you submit, EPI reviews and commits (maker-checker). The merchant receives an anti-vishing verification code on bank/masterfile submissions.
| Field | Type | Description |
|---|---|---|
bank_namerequired | string | New bank name. |
routingrequired | string | 9-digit ABA routing. |
accountrequired | string | New deposit account number (4–17 digits). |
account_typeoptional | string | CHECKING (default) | SAVINGS. |
account_holder_nameoptional | string | Name on the account (≤22 chars, NACHA). |
voided_checkoptional | object | Optional supporting doc: { filename, content_base64, mime_type }. |
notesoptional | string | Free-text note for the reviewer. |
| Field | Type | Description |
|---|---|---|
dba_nameoptional | string | New DBA name. |
contact_nameoptional | string | New merchant contact name. |
emailoptional | string | New merchant email. |
phone / legal_phoneoptional | string | New DBA / legal phone. |
addressoptional | object | DBA address: { line1, city, state, zip }. |
legal_addressoptional | object | Legal address: { line1, city, state, zip }. |
descriptionoptional | string | Free-text for an "other" change the reviewer applies by hand. |
At least one masterfile field is required.
| Field | Type | Description |
|---|---|---|
new_valuesrequired | object | Sparse map of pricing field key → string value. Unknown keys are ignored. Pure decreases skip merchant approval; any increase routes to the merchant. One in-flight change per merchant. |
effective_start_dateoptional | string | Optional YYYY-MM-DD. |
effective_end_dateoptional | string | Optional YYYY-MM-DD. |
notesoptional | string | Free-text note. |
GET /change-requests (+ /{id}, changes:read) tracks any request through review to commit. The response includes the pricing Old→New diff; bank and masterfile fields are omitted.
# bank (deposit account) change – GIACT runs on submit
curl -X POST "https://www.cygma.cloud/api/v1/merchants/<id>/bank-change" \
-H "Authorization: Bearer <your-key>" -H "Content-Type: application/json" \
-d '{ "bank_name": "Chase", "routing": "021000021", "account": "123456789", "account_type": "CHECKING" }'
# masterfile (AMF) change – any subset of fields
curl -X POST "https://www.cygma.cloud/api/v1/merchants/<id>/masterfile-change" \
-H "Authorization: Bearer <your-key>" -H "Content-Type: application/json" \
-d '{ "email": "owner@merchant.com", "phone": "512-555-0100" }'
# pricing change – sparse map of field -> value
curl -X POST "https://www.cygma.cloud/api/v1/merchants/<id>/pricing-change" \
-H "Authorization: Bearer <your-key>" -H "Content-Type: application/json" \
-d '{ "new_values": { "monthly_fee": "9.95", "amex_qual_rate": "2.89" } }'
# track it
curl "https://www.cygma.cloud/api/v1/change-requests?merchant_id=<id>" -H "Authorization: Bearer <your-key>"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>"
Statements (Cygma)
Pull a merchant’s Cygma processing statement. Requires the statements:readscope; the merchant must be in the key’s book of business. The list returns available periods (YYYYMM) with a ready-to-fetch pdf_url; the PDF endpoint streams the same branded statement the dashboard renders. 404 when no statement exists for that period.
# list the periods a merchant has a statement for curl "https://www.cygma.cloud/api/v1/merchants/<merchant-id>/statements" \ -H "Authorization: Bearer <your-key>" # download one period's PDF curl "https://www.cygma.cloud/api/v1/merchants/<merchant-id>/statements/202607/pdf" \ -H "Authorization: Bearer <your-key>" -o statement.pdf
Equipment catalog
The active device / SKU catalog that backs order line items. Requires orders:read. Filter with q (manufacturer / model / name) and category. This is reference data – not merchant-scoped.
curl "https://www.cygma.cloud/api/v1/catalog/equipment?q=clover&category=SMART_POS" \ -H "Authorization: Bearer <your-key>"
Pricing templates
The rate cards this key’s office can attach to a boarding application – the office’s own templates, its division’s, and the shared EPI library. Requires applications:read. Each row’s scope is office or epi_library. The same id is shown on every template in ISO360 (Pricing Templates) with a copy-to-clipboard API chip, so you can grab it from the dashboard instead of paging this endpoint.
curl "https://www.cygma.cloud/api/v1/pricing-templates" \ -H "Authorization: Bearer <your-key>"
Equipment templates
The saved equipment bundles this key’s office can attach to a boarding application (pass the id as equipment_template_id on POST/PATCH /applications). Attaching one materializes a draft order with the template’s lines. Requires applications:read; same office → division → EPI-library scope as pricing templates. The id is also shown on every template in ISO360 (Equipment Templates) with a copy-to-clipboard API chip.
curl "https://www.cygma.cloud/api/v1/equipment-templates" \ -H "Authorization: Bearer <your-key>"
Reference lists
The lookups partners need to set valid values on a boarding application. Requires reference:read. mcc is searchable + paginated; business-types is the fixed tax-classification list.
curl "https://www.cygma.cloud/api/v1/reference/mcc?q=restaurant" \ -H "Authorization: Bearer <your-key>" curl "https://www.cygma.cloud/api/v1/reference/business-types" \ -H "Authorization: Bearer <your-key>"
Webinar library
EPI’s webinar recordings, the same index agents see in ISO360 under Marketing. Requires webinars:read. The list is not merchant-scoped — it is the same catalog for every key.
GET /webinars is paginated and takes four filters, which combine: q (title, presenter, and description), category (a value from /webinars/categories), year (YYYY), and since (YYYY-MM-DD, inclusive). Results are newest first.
embed_url is a youtube-nocookie player URL you can drop straight into an iframe; thumbnail_url is the poster frame. Both are null when youtube_video_id is null. Recordings from 2016–2017 predate the YouTube channel and carry only bankcard_forum_url, a link to the Bankcard Forum thread; there is nothing to embed for those.
Only recordings EPI has published are returned. Videos are served by YouTube, not by this API — these endpoints return the index.
curl "https://www.cygma.cloud/api/v1/webinars?category=FEE_PROGRAMS&page_size=2" -H "Authorization: Bearer <your-key>" curl "https://www.cygma.cloud/api/v1/webinars?q=surcharge&year=2024" -H "Authorization: Bearer <your-key>" curl "https://www.cygma.cloud/api/v1/webinars?since=2026-01-01" -H "Authorization: Bearer <your-key>" curl "https://www.cygma.cloud/api/v1/webinars/playlists" -H "Authorization: Bearer <your-key>" curl "https://www.cygma.cloud/api/v1/webinars/categories" -H "Authorization: Bearer <your-key>"
Chargeback detail
One chargeback with its event timeline and evidence-document list – the detail behind a row from GET /chargebacks. Requires chargebacks:read; the merchant must be in the key’s book.
curl "https://www.cygma.cloud/api/v1/chargebacks/<chargeback-id>" \ -H "Authorization: Bearer <your-key>"
Residuals
Per-merchant residual line items for your user, office, and division allocations, by period. Requires residuals:read. Filter with period (YYYYMM) and mid.
curl "https://www.cygma.cloud/api/v1/residuals?period=202607" \ -H "Authorization: Bearer <your-key>"
Equipment orders
Price and place hardware orders. POST /pos-orders/quote (orders:read) computes the money exactly as the in-app order form does – office equipment markup, shipping derived from the SKUs + method, and the active sales-tax rate – without placing anything. POST /pos-orders (orders:write) places a Clover/Exatouch PURCHASE order as an agent using the same math. Look up SKUs from the catalog.
| Field | Type | Description |
|---|---|---|
brandrequired | string | CLOVER | EXATOUCH. |
merchant_idrequired | string | Merchant the order is for (must be in your book). |
line_itemsrequired | object[] | Each: { sku_id, qty }. Placement SKUs are rejected (they’re provisioned, not sold). |
ship_methodoptional | string | GROUND (default) | TWO_DAY | NEXT_DAY. |
ship_to_same_as_dbaoptional | boolean | Create only. Default true – otherwise supply the ship_* fields. |
ship_address / ship_city / ship_state / ship_zip / ship_phoneoptional | string | Create only, when not shipping to the DBA address. |
ship_stateoptional | string | Quote only – used for the sales-tax rate when different from the DBA state. |
notesoptional | string | Create only. Free-text. |
| Field | Type | Description |
|---|---|---|
merchant_idrequired | string | Merchant the TSF is for. |
account_typerequired | string | NEW_ACCOUNT | EXISTING_ADD_ORDER | SWAP_SEND_RCT. |
ship_to_typerequired | string | ISO | MERCHANT | OTHER | LOCAL_PICKUP | NO_SHIPPING. |
ship_methodrequired | string | GROUND | EXPRESS_2DAY | STD_OVERNIGHT | … |
bill_methodrequired | string | ACH_MERCHANT | CC_MERCHANT | CC_AGENT_ON_FILE | CC_AGENT_NEW | ACH_AGENT_ON_FILE. |
line_itemsrequired | object[] | Each: { equipment_id, frontend_id, quantity } (+ optional markup_amount, serial_number, programming). |
programoptional | string | PURCHASE (default) | EPI_FREE_TERMINAL. |
software / procharge / pos_gatewayoptional | object | Optional programming blocks (gateway type, frontend/backend, datawire mode, VAR-sheet email, etc.). |
submit_immediatelyoptional | boolean | Submit on create instead of leaving a draft. |
notesoptional | string | Free-text. |
Placing a terminal order requires the ORDER_POS permission on the key’s creator.
| Field | Type | Description |
|---|---|---|
merchant_idrequired | string | Merchant the order is for. |
case_sizerequired | string | HALF | FULL. |
shippingrequired | object | { name, address, city, state, zip, method } (+ optional suite, phone). |
paymentrequired | object | { method } – MAP | ACH | CC | INVOICE | AGENT_NET30 | AGENT_CARD (+ optional card_token). |
kindoptional | string | PAPER (default) | SUPPLIES | POS_PAPER. |
paper_typeoptional | string | Catalog paper code (for PAPER / POS_PAPER). |
item_kindoptional | string | For SUPPLIES: CHECK_PRESENTER | INK_RIBBON | OTHER. |
package_countoptional | integer | Boxes to ship (1–20). Default 1. |
notesoptional | string | Free-text. |
Order visibility (all orders:read, scoped to your book):
GET /pos-orders·GET /pos-orders/{id}– Clover / Exatouch orders.GET /terminal-orders·{id}– Terminal Setup Form / terminal orders (file-build → deployment).GET /supply-orders·{id}– supplies / paper orders (?kind=PAPER).
Placement SKUs can’t be purchased through the API (they’re provisioned, not sold). Placing terminal-setup and supply/paper orders via the API is a later addition – for now those two are read-only here.
# quote a Clover order (no order placed)
curl -X POST "https://www.cygma.cloud/api/v1/pos-orders/quote" \
-H "Authorization: Bearer <your-key>" -H "Content-Type: application/json" \
-d '{ "brand": "CLOVER", "merchant_id": "<merchant-id>",
"line_items": [ { "sku_id": "<sku-id>", "qty": 1 } ], "ship_method": "GROUND" }'
# place it
curl -X POST "https://www.cygma.cloud/api/v1/pos-orders" \
-H "Authorization: Bearer <your-key>" -H "Content-Type: application/json" \
-d '{ "brand": "CLOVER", "merchant_id": "<merchant-id>",
"line_items": [ { "sku_id": "<sku-id>", "qty": 1 } ] }'Terminal provisioning
Mint a merchant’s network identity and pull the paperwork. GET /merchants/{id}/terminals (terminals:read) lists devices and their V#/Cygma TID. Minting needs terminals:write.
| Field | Type | Description |
|---|---|---|
midoptional | string | Cygma-BIN MID (400321 / 411763 prefix). Falls back to the merchant's stored MID. |
mccoptional | string | 4-digit MCC. Falls back to the merchant's sic_code. Required to provision. |
Idempotent – a merchant that already has a primary TID returns 409 already_provisioned. A non-Cygma-BIN MID returns 422 unsupported_mid; a missing MCC returns 422 needs_mcc. Success also sends the M360 invite.
| Field | Type | Description |
|---|---|---|
labeloptional | string | Friendly device label. |
mccoptional | string | 4-digit MCC (defaults to the merchant's). |
serial_numberoptional | string | Device serial. |
linked_terminal_idoptional | string | Physical terminal id to attach to (null for standalone). |
POST /terminals/{id}/tsys-vnumber– mint/adopt the TSYS V# (Sierra TID) for a device. Idempotent; no body.GET /terminals/{id}/var-sheet– the branded TSYS VAR sheet as a PDF, regenerated fresh.
# provision the merchant's primary Cygma TID from its MID + MCC
curl -X POST "https://www.cygma.cloud/api/v1/merchants/<id>/cygma-tid" \
-H "Authorization: Bearer <your-key>" -H "Content-Type: application/json" \
-d '{ "mid": "400321000001234", "mcc": "5812" }'
# mint a TSYS V# for a device, and pull its VAR sheet
curl -X POST "https://www.cygma.cloud/api/v1/terminals/<terminal-id>/tsys-vnumber" -H "Authorization: Bearer <your-key>"
curl "https://www.cygma.cloud/api/v1/terminals/<terminal-id>/var-sheet" -H "Authorization: Bearer <your-key>" -o var-sheet.pdfWebhooks
Get pushed the moment something happens instead of polling. Register an endpoint (webhooks:manage) and it receives events for the merchants in your book only. The secret is returned once; verify each delivery with HMAC-SHA256(sha256(secret), rawBody) against X-Webhook-Signature. Failed deliveries retry with backoff (1m → 5m → 30m → 2h → 12h, then give up).
| Field | Type | Description |
|---|---|---|
urlrequired | string | Your https:// endpoint. Non-https is rejected. |
eventsoptional | string[] | Which events to receive (see the list below), or omit for "*" (all). |
nameoptional | string | A label for your reference. |
Events: merchant.boarded, merchant.mid_assigned (the processor MID lands at approval - the payload carries the assigned mid), application.submitted, application.approved, application.declined, change_request.updated (a bank / masterfile / pricing change was confirmed, declined, or committed - includes auto_committed when a Plaid-verified bank change processed without manual review), pos_order.created, pos_order.shipped, chargeback.received, invoice.paid, merchant.churn_high – or "*" for all.
GET /webhooks·GET /webhooks/{id}– list / inspect (with recent deliveries).DELETE /webhooks/{id}– remove an endpoint.POST /webhooks/{id}/test– send a syntheticwebhook.testto check your handler.
curl -X POST "https://www.cygma.cloud/api/v1/webhooks" \
-H "Authorization: Bearer <your-key>" -H "Content-Type: application/json" \
-d '{ "url": "https://yourapp.com/hooks/epi",
"events": ["merchant.boarded", "application.approved", "chargeback.received"] }'Routing-number lookup
Resolve a 9-digit ABA routing number to its financial institution. Data comes from the Federal ReserveFedACH Participants Directory, refreshed monthly. 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.