Agent platform · REST

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.

Overview

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.
New here? Get a free read-only key.

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 sandbox
Bearer key, per office

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 returns 429.
  • 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.

↑ Request – what you send
# Every request carries your office key as a bearer token.
curl https://www.cygma.cloud/api/v1/applications \
  -H "Authorization: Bearer epi_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
Self-serve read-only key

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, and webinars: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 403 on those.
  • Capped. Five active keys per email address, and a global daily ceiling. Lost a key? Request another.
↑ Request – what you send
# 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>"
2 steps

Quickstart

  1. Create your office key in My Office → ISO360 API.
  2. 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.
↑ Request – what you send
# 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"
POST /api/v1/proposals/analyze-statement

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.

Requestmultipart/form-data:

FieldRequiredNotes
statementyesPDF file, 25 MB max.
merchant_nameyesBusiness name.
mccno4-digit MCC. Inferred when omitted.
address, city, state, zipnoPhysical address. Blanks are backfilled from the statement.
dba_name, legal_name, phone, emailnoContact 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).

↑ Request – what you send
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"
POST /api/v1/proposals

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 PDFsend itmark 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_id or lead_id).
  • GET /proposals · GET /proposals/{id} – list / fetch.
  • POST /proposals/{id}/versions – save a priced version (knobs.pricing_model required).
  • POST /proposals/{id}/pdf – generate the merchant PDF; GET the same path downloads it.
  • POST /proposals/{id}/send – email it (to_email) and mark SENT.
  • POST /proposals/{id}/accept – mark ACCEPTED.
POST /proposals – open a draft
FieldTypeDescription
merchant_idoptionalstringAnchor to an existing merchant. Provide this OR lead_id (exactly one).
lead_idoptionalstringAnchor to a lead instead of a merchant.
target_savings_pctoptionalnumberOptional savings target to prompt the pricing engine.
target_savings_dollarsoptionalnumberOptional dollar savings target.
POST /proposals/{id}/versions – price it
FieldTypeDescription
knobsrequiredobjectPricing 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_processingoptionalobjectOptional statement facts for the savings math: annual_volume, avg_ticket, effective_rate_pct, annual_fees, processor, card mix.
modeoptionalstringcreate (default, new version) | update_latest (overwrite the latest).
POST /proposals/{id}/send
FieldTypeDescription
to_emailrequiredstringRecipient for the merchant-facing PDF. Marks the proposal SENT.

The response and the PDF carry the merchant-facing pricing figures.

↑ Request – what you send
# 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" }'
POST /api/v1/applications

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.

POST /applications – top level
FieldTypeDescription
businessrequiredobjectBusiness identity block (see below).
preferred_backendoptionalstringProcessor to board on: NORTH (default), OMAHA, or CYGMA.
ownersoptionalobject[]Beneficial owners / signers. (≥1 required to submit.)
bankoptionalobjectDeposit account. (Required to submit.)
riskoptionalobjectProcessing volumes + sales mix (feeds underwriting).
pricing_template_idoptionalstringAttach 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_idoptionalstringAttach 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.
business
FieldTypeDescription
dba_namerequiredstringDoing-business-as name.
legal_namerequiredstringLegal entity name (W-9 line 1).
addressrequiredstringPhysical / DBA street address.
cityrequiredstringDBA city.
staterequiredstring2-letter state.
ziprequiredstringDBA ZIP.
business_start_dateoptionalstringYYYY-MM-DD. (Required to submit.)
mccoptionalstring4-digit merchant category code. (Required to provision a Cygma TID.) Look one up via /reference/mcc.
einoptionalstringFederal Tax ID / EIN. Encrypted at rest; only last-4 is ever returned.
tin_typeoptionalstringSSN | EIN | ITIN.
tax_classificationoptionalstringEntity type – see /reference/business-types.
business_typeoptionalstringFree-text business description.
phoneoptionalstringDBA phone.
emailoptionalstringMerchant contact email (lowercased).
websiteoptionalstringMerchant website.
owners[] – each owner
FieldTypeDescription
full_namerequiredstringOwner's full legal name.
ownership_pctoptionalnumberPercent ownership. Owners ≥25% must have SSN, DOB, and full address to submit.
ssnoptionalstring9-digit SSN. Encrypted at rest. (Required to submit for owners ≥25% + control persons.)
doboptionalstringYYYY-MM-DD. Encrypted at rest. (Required to submit, same rule.)
address / city / state / zipoptionalstringOwner home address. (Required to submit, same rule.)
is_signeroptionalbooleanMarks a signing owner. Signers must total ≥50% equity to submit (auto-promoted if short).
is_control_onlyoptionalbooleanFinCEN control person with no equity – still needs SSN/DOB/address.
titleoptionalstringOwner title (e.g. President).
email / phoneoptionalstringOwner contact.
bank (primary deposit account)
FieldTypeDescription
routingoptionalstring9-digit ABA routing. (Required to submit.) Validate via /banking/routing.
accountoptionalstringDeposit account number. (Required to submit.)
account_typeoptionalstringCHECKING (default) | SAVINGS.
bank_nameoptionalstringBank name (recommended).
risk (all optional – feeds underwriting)
FieldTypeDescription
annual_volumeoptionalnumberAnnual card volume, dollars.
average_ticketoptionalnumberAverage sale, dollars.
high_ticketoptionalnumberHighest single sale, dollars.
pct_swiped / pct_keyed / pct_moto / pct_internetoptionalnumberEntry mix %, should total 100.
pct_retail / pct_b2b / pct_mobileoptionalnumberSales-location mix %.
POST /applications/{id}/submit
FieldTypeDescription
signature_namerequiredstringThe signer's typed name (the e-signature).
signature_bloboptionalstringOptional signature image as a data URL.
confirmation_signature_nameoptionalstringDefaults to signature_name.
confirmation_signature_bloboptionalstringDefaults 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.

↑ Request – what you send
# 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" }'
POST /api/v1/merchants/{id}/{bank-change,pricing-change,masterfile-change}

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.

POST /merchants/{id}/bank-change
FieldTypeDescription
bank_namerequiredstringNew bank name.
routingrequiredstring9-digit ABA routing.
accountrequiredstringNew deposit account number (4–17 digits).
account_typeoptionalstringCHECKING (default) | SAVINGS.
account_holder_nameoptionalstringName on the account (≤22 chars, NACHA).
voided_checkoptionalobjectOptional supporting doc: { filename, content_base64, mime_type }.
notesoptionalstringFree-text note for the reviewer.
POST /merchants/{id}/masterfile-change (send only what changes)
FieldTypeDescription
dba_nameoptionalstringNew DBA name.
contact_nameoptionalstringNew merchant contact name.
emailoptionalstringNew merchant email.
phone / legal_phoneoptionalstringNew DBA / legal phone.
addressoptionalobjectDBA address: { line1, city, state, zip }.
legal_addressoptionalobjectLegal address: { line1, city, state, zip }.
descriptionoptionalstringFree-text for an "other" change the reviewer applies by hand.

At least one masterfile field is required.

POST /merchants/{id}/pricing-change
FieldTypeDescription
new_valuesrequiredobjectSparse 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_dateoptionalstringOptional YYYY-MM-DD.
effective_end_dateoptionalstringOptional YYYY-MM-DD.
notesoptionalstringFree-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.

↑ Request – what you send
# 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>"
GET /api/v1/applications

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.

↑ Request – what you send
curl "https://www.cygma.cloud/api/v1/applications?page=1&page_size=25&status=SUBMITTED" \
  -H "Authorization: Bearer <your-key>"
GET /api/v1/merchants/{id}/statements

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.

↑ Request – what you send
# 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
GET /api/v1/catalog/equipment

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.

↑ Request – what you send
curl "https://www.cygma.cloud/api/v1/catalog/equipment?q=clover&category=SMART_POS" \
  -H "Authorization: Bearer <your-key>"
GET /api/v1/pricing-templates

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.

↑ Request – what you send
curl "https://www.cygma.cloud/api/v1/pricing-templates" \
  -H "Authorization: Bearer <your-key>"
GET /api/v1/equipment-templates

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.

↑ Request – what you send
curl "https://www.cygma.cloud/api/v1/equipment-templates" \
  -H "Authorization: Bearer <your-key>"
GET /api/v1/reference/{mcc|business-types}

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.

↑ Request – what you send
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>"
GET /api/v1/webinars · /webinars/playlists · /webinars/categories

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.

↑ Request – what you send
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>"
GET /api/v1/chargebacks/{id}

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.

↑ Request – what you send
curl "https://www.cygma.cloud/api/v1/chargebacks/<chargeback-id>" \
  -H "Authorization: Bearer <your-key>"
GET /api/v1/residuals

Residuals

Per-merchant residual line items for your user, office, and division allocations, by period. Requires residuals:read. Filter with period (YYYYMM) and mid.

↑ Request – what you send
curl "https://www.cygma.cloud/api/v1/residuals?period=202607" \
  -H "Authorization: Bearer <your-key>"
POST /api/v1/pos-orders

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.

POS – /pos-orders/quote and /pos-orders (create)
FieldTypeDescription
brandrequiredstringCLOVER | EXATOUCH.
merchant_idrequiredstringMerchant the order is for (must be in your book).
line_itemsrequiredobject[]Each: { sku_id, qty }. Placement SKUs are rejected (they’re provisioned, not sold).
ship_methodoptionalstringGROUND (default) | TWO_DAY | NEXT_DAY.
ship_to_same_as_dbaoptionalbooleanCreate only. Default true – otherwise supply the ship_* fields.
ship_address / ship_city / ship_state / ship_zip / ship_phoneoptionalstringCreate only, when not shipping to the DBA address.
ship_stateoptionalstringQuote only – used for the sales-tax rate when different from the DBA state.
notesoptionalstringCreate only. Free-text.
POST /terminal-orders (Terminal Setup Form)
FieldTypeDescription
merchant_idrequiredstringMerchant the TSF is for.
account_typerequiredstringNEW_ACCOUNT | EXISTING_ADD_ORDER | SWAP_SEND_RCT.
ship_to_typerequiredstringISO | MERCHANT | OTHER | LOCAL_PICKUP | NO_SHIPPING.
ship_methodrequiredstringGROUND | EXPRESS_2DAY | STD_OVERNIGHT | …
bill_methodrequiredstringACH_MERCHANT | CC_MERCHANT | CC_AGENT_ON_FILE | CC_AGENT_NEW | ACH_AGENT_ON_FILE.
line_itemsrequiredobject[]Each: { equipment_id, frontend_id, quantity } (+ optional markup_amount, serial_number, programming).
programoptionalstringPURCHASE (default) | EPI_FREE_TERMINAL.
software / procharge / pos_gatewayoptionalobjectOptional programming blocks (gateway type, frontend/backend, datawire mode, VAR-sheet email, etc.).
submit_immediatelyoptionalbooleanSubmit on create instead of leaving a draft.
notesoptionalstringFree-text.

Placing a terminal order requires the ORDER_POS permission on the key’s creator.

POST /supply-orders (paper / supplies)
FieldTypeDescription
merchant_idrequiredstringMerchant the order is for.
case_sizerequiredstringHALF | FULL.
shippingrequiredobject{ name, address, city, state, zip, method } (+ optional suite, phone).
paymentrequiredobject{ method }MAP | ACH | CC | INVOICE | AGENT_NET30 | AGENT_CARD (+ optional card_token).
kindoptionalstringPAPER (default) | SUPPLIES | POS_PAPER.
paper_typeoptionalstringCatalog paper code (for PAPER / POS_PAPER).
item_kindoptionalstringFor SUPPLIES: CHECK_PRESENTER | INK_RIBBON | OTHER.
package_countoptionalintegerBoxes to ship (1–20). Default 1.
notesoptionalstringFree-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.

↑ Request – what you send
# 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 } ] }'
POST /api/v1/merchants/{id}/cygma-tid

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.

POST /merchants/{id}/cygma-tid – primary TID (standalone board)
FieldTypeDescription
midoptionalstringCygma-BIN MID (400321 / 411763 prefix). Falls back to the merchant's stored MID.
mccoptionalstring4-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.

POST /merchants/{id}/cygma-device-tid – additional device TID
FieldTypeDescription
labeloptionalstringFriendly device label.
mccoptionalstring4-digit MCC (defaults to the merchant's).
serial_numberoptionalstringDevice serial.
linked_terminal_idoptionalstringPhysical 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.
↑ Request – what you send
# 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.pdf
POST /api/v1/webhooks

Webhooks

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).

POST /webhooks
FieldTypeDescription
urlrequiredstringYour https:// endpoint. Non-https is rejected.
eventsoptionalstring[]Which events to receive (see the list below), or omit for "*" (all).
nameoptionalstringA 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 synthetic webhook.test to check your handler.
↑ Request – what you send
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"] }'
GET /api/v1/banking/routing/{routing}

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).

Try itsimulated example – no live call

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.

↑ Request – what you send
curl "https://www.cygma.cloud/api/v1/banking/routing/121000248" \
  -H "Authorization: Bearer <your-key>"
GET /api/v1/banking/bin/{bin} – test data

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).

Try itsimulated example – no live call, test data only

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.

↑ Request – what you send
curl "https://www.cygma.cloud/api/v1/banking/bin/486208" \
  -H "Authorization: Bearer <your-key>"
Success status codes

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:

StatusMeaning
200OK — a read, lookup, or list succeeded; the resource is under data.
201Created — a new resource (e.g. a proposal from an analyzed statement) was created.
202Accepted — the request was queued for asynchronous processing.
↑ Request – what you send
// 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 }
HTTP status + { error }

Errors

StatusMeaning
401Missing, malformed, invalid, or revoked key.
403Key lacks the required scope (or isn’t office-bound).
413Statement file too large (25 MB max).
415Body isn’t multipart, or the file isn’t a PDF.
422Missing a required field, or the statement couldn’t be parsed.
429Rate limit exceeded – back off and retry.
↑ Request – what you send
// Errors return a JSON body with a single `error` string.
{ "error": "Missing required scope: proposals:create" }
Stability

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.