安全なPOSシステム — すべてのアカウントを二要素認証で保護
jadePOSポイントオブセール

jadePOS API Reference

ストアを自動化:決済の作成、商品管理、台帳の参照。アカウントのAPIキーで認証します。

ConventionsGET /pingGET /meGET /productsPOST /productsGET /ordersGET /transactionsPOST /chargesCustomersPlansSubscriptionsGET /invoicesCard validationTest cardsGateway relayWebhooksStatus codes

Conventions

Base URLhttps://jadepos.store
AuthenticationAuthorization: Bearer jade_… — create and revoke keys under Account → API keys. Keys are stored hashed; the value is shown once.
Request bodiesapplication/x-www-form-urlencoded form fields (as in the curl examples)
ResponsesJSON, application/json; charset=utf-8
AmountsIntegers in minor units: 1250 = $12.50. JPY/KRW have no minor unit — pass whole yen/won.
Rate limitsReads 60/min, product writes 60/min, charges 30/min (per IP). Exceeding answers 429 {"error":"rate_limited"}.
ErrorsNon-2xx responses carry {"error":"human readable reason"}

GET/api/v1/ping

Liveness check. The only endpoint that needs no key.

curl https://jadepos.store/api/v1/ping
→ {"ok":true,"service":"jadepos.store"}

GET/api/v1/me

The merchant who owns the key — use it to verify a key and read your ledger currency.

curl -H "Authorization: Bearer jade_..." https://jadepos.store/api/v1/me
→ {"username":"demo","business_name":"Jade Cafe","currency":"USD"}

GET/api/v1/products

Your full catalog, active and archived.

curl -H "Authorization: Bearer jade_..." https://jadepos.store/api/v1/products
→ {"products":[{"sku":"latte","name":"Latte","description":"Double shot",
     "kind":"product","price_cents":450,"currency":"USD","tax_percent":8.5,
     "requires_shipping":false,"active":true}]}

POST/api/v1/products

Create a catalog item, or update it if the SKU already exists (upsert).

FieldDescription
skurequiredLetters, digits, dot, dash, underscore; max 40. The upsert key.
namerequiredDisplay name, max 120 chars.
price_centsrequiredInteger minor units.
descriptionoptionalUp to 500 chars.
kindoptionalproduct (default) or service.
currencyoptionalDefaults to your ledger currency.
tax_percentoptionalPer-line tax rate, e.g. 8.5. Default 0.
requires_shippingoptional1 to demand a shipping address at checkout. Default 0.
curl -X POST https://jadepos.store/api/v1/products \
  -H "Authorization: Bearer jade_..." \
  -d sku=beans-1kg -d "name=Coffee Beans 1kg" -d price_cents=1899 -d requires_shipping=1
→ {"ok":true,"sku":"beans-1kg","created":true}

GET/api/v1/orders

Your orders, newest first, 50 per page. Draft carts are never included.

FieldDescription
statusoptionalpaid, pending, declined, refunded, void; empty = all.
pageoptional1-based page number.
curl -H "Authorization: Bearer jade_..." "https://jadepos.store/api/v1/orders?status=paid&page=1"
→ {"orders":[{"ref":"JP-2524f49c22","status":"paid","total_cents":800,
     "currency":"USD","customer_name":"Lin","created_at":"2026-08-27 09:12:44",
     "paid_at":"2026-08-27 09:12:45"}]}

GET/api/v1/transactions

The money ledger: one row per charge or refund attempt, newest first, 50 per page (page param). Card rows carry only the brand and last four digits — never the full number.

curl -H "Authorization: Bearer jade_..." https://jadepos.store/api/v1/transactions
→ {"transactions":[{"tx_ref":"TX-63505a62be6f","order_ref":"JP-e4a6c4dd8d",
     "kind":"charge","method":"card","mode":"test","status":"approved",
     "amount_cents":1250,"currency":"USD","card_brand":"amex",
     "card_last4":"0005","auth_code":"F94FAC","created_at":"2026-08-26 16:04:46"}]}

POST/api/v1/charges

Validate and charge a card, or record a PayPal / Cash App payment you already received. Creates a one-line order, charges it, and returns the outcome with a public receipt URL.

Common fields

FieldDescription
methodrequiredcard, paypal, or cashapp. Wallets must be enabled in Payments.
amount_centsrequiredPositive integer minor units.
currencyoptionalDefaults to your ledger currency.
descriptionoptionalLine-item text on the order and receipt.
customer_nameoptionalShown on the order.
customer_emailoptionalThe receipt link is emailed when the mail watcher is configured.
ship_name … ship_countryoptionalShipping address: ship_name, ship_address1, ship_address2, ship_city, ship_region, ship_postal, ship_country. When ship_address1 is present the order gets a printable label at /label?ref=… (merchant login).

Card fields (method=card)

FieldDescription
card_numberrequiredDigits, spaces and dashes allowed. Never stored.
exp_monthrequired1–12.
exp_yearrequired4-digit (2030) or 2-digit (30).
cvvrequired3 digits; 4 for American Express. Never stored.

Wallet fields (method=paypal | cashapp)

FieldDescription
referenceoptionalYour note of the wallet payment (transaction id, screenshot ref).
curl -X POST https://jadepos.store/api/v1/charges \
  -H "Authorization: Bearer jade_..." \
  -d method=card -d amount_cents=1250 -d currency=USD \
  -d "description=Latte x2" \
  -d card_number=4242424242424242 -d exp_month=12 -d exp_year=2030 -d cvv=123

→ 200 {"approved":true,"order_ref":"JP-1a2b3c4d5e","auth_code":"A1B2C3",
       "card_brand":"visa","card_last4":"4242","amount_cents":1250,
       "currency":"USD","receipt_url":"https://jadepos.store/receipt?ref=JP-1a2b3c4d5e"}

→ 402 {"approved":false,"order_ref":"JP-8f54674005","card_brand":"visa",
       "error":"Card declined (test card)"}

Whether a card charge is simulated (test) or sent to your processor (live) follows the card method's mode in Payments. The receipt URL is public but unguessable; it shows no card data beyond brand + last4.

Customers

The customer book subscriptions bill against.

GET/api/v1/customers

Search/list your customers, newest first, 50 per page.

FieldDescription
qoptionalSearch name or email (substring match).
pageoptional1-based page number.
curl -H "Authorization: Bearer jade_..." "https://jadepos.store/api/v1/customers?q=lin"
→ {"customers":[{"id":3,"name":"Lin","email":"lin@example.com",
     "phone":"","status":"active","created_at":"2026-08-27 09:12:44"}]}

POST/api/v1/customers

Create a customer, or update one if id is given (upsert).

FieldDescription
idoptional0 or absent creates a new customer; a positive id updates that one.
namerequiredMax 120 chars.
email, phone, address1, address2, city, region, postal, country, notesoptional
curl -X POST https://jadepos.store/api/v1/customers \
  -H "Authorization: Bearer jade_..." \
  -d "name=Lin" -d email=lin@example.com
→ {"ok":true,"id":3,"created":true}

GET/api/v1/customers/get

One customer, by id or email.

FieldDescription
id or emailrequiredOne of the two.
curl -H "Authorization: Bearer jade_..." "https://jadepos.store/api/v1/customers/get?email=lin@example.com"

Plans

Pricing blueprints subscriptions are created against. Editing a plan never changes what an existing subscriber owes — price and interval are snapshotted onto the subscription at signup.

GET/api/v1/plans

All plans, active and archived.

curl -H "Authorization: Bearer jade_..." https://jadepos.store/api/v1/plans
→ {"plans":[{"code":"pro-monthly","name":"Pro","description":"",
     "price_cents":1999,"currency":"USD","interval_unit":"month",
     "interval_count":1,"trial_days":14,"active":true}]}

POST/api/v1/plans

Create a plan, or update one if code already exists (upsert).

FieldDescription
coderequiredLetters, digits, dot, dash, underscore; max 40. The upsert key.
namerequiredDisplay name.
price_centsrequiredInteger minor units, charged each cycle.
descriptionoptional
currencyoptionalDefaults to your ledger currency.
interval_unitoptionalday, week, month (default), or year.
interval_countoptionalBill every N units. Default 1.
trial_daysoptionalFree trial before the first charge. Default 0.
curl -X POST https://jadepos.store/api/v1/plans \
  -H "Authorization: Bearer jade_..." \
  -d code=pro-monthly -d name=Pro -d price_cents=1999 -d trial_days=14
→ {"ok":true,"code":"pro-monthly","created":true}

Subscriptions

Recurring billing engine. Card subscriptions auto-charge (test mode simulates; live mode tokenizes the card through your gateway relay and charges the token each cycle — the PAN is never stored). PayPal/Cash App subscriptions create a pending invoice each cycle that you confirm with /confirm-payment once you've actually been paid, same as a one-off wallet checkout.

GET/api/v1/subscriptions

Your subscriptions, newest first, 50 per page.

FieldDescription
statusoptionaltrialing, active, past_due, paused, canceled; empty = all.
pageoptional1-based page number.
curl -H "Authorization: Bearer jade_..." "https://jadepos.store/api/v1/subscriptions?status=active"

GET/api/v1/subscriptions/get

One subscription plus its full invoice history.

FieldDescription
refrequiredThe SUB-... reference.
curl -H "Authorization: Bearer jade_..." "https://jadepos.store/api/v1/subscriptions/get?ref=SUB-1a2b3c4d5e"
→ {"ref":"SUB-1a2b3c4d5e","status":"active","method":"card",
     "plan_name":"Pro","price_cents":1999,"currency":"USD",
     "current_period_end":"2026-09-27 09:12:44","next_billing_at":"2026-09-27 09:12:44",
     "cancel_at_period_end":false,"failed_attempts":0,
     "invoices":[{"ref":"INV-63505a","period_start":"...","period_end":"...",
        "amount_cents":1999,"status":"paid","attempt_count":1,"paid_at":"..."}]}

POST/api/v1/subscriptions

Start a subscription. Non-trial plans are charged immediately — the response reflects that first attempt.

FieldDescription
plan_coderequired
customer_idoptionalAn existing customer id.
customer_name, customer_emailoptionalUsed instead of customer_id — finds an existing customer by email or creates one.
methodoptionalcard (default), paypal, or cashapp — must be enabled in Payments.
payment_method_idoptionalReuse a card already on file for this customer instead of the fields below.
card_number, exp_month, exp_year, cvvoptionalSame rules as /charges. Never stored — only brand/last4 (and a gateway token in live mode).
curl -X POST https://jadepos.store/api/v1/subscriptions \
  -H "Authorization: Bearer jade_..." \
  -d plan_code=pro-monthly -d customer_email=lin@example.com -d "customer_name=Lin" \
  -d card_number=4242424242424242 -d exp_month=12 -d exp_year=2030 -d cvv=123
→ {"ok":true,"ref":"SUB-1a2b3c4d5e","status":"active",
     "current_period_end":"2026-09-27 09:12:44","next_billing_at":"2026-09-27 09:12:44"}

POST/api/v1/subscriptions/cancel

Cancel a subscription, immediately or at the end of the current period.

FieldDescription
refrequired
at_period_endoptional1 to keep it active through the current period, then auto-cancel with no further charge. Default 0 (cancels now).
reasonoptional
curl -X POST https://jadepos.store/api/v1/subscriptions/cancel \
  -H "Authorization: Bearer jade_..." -d ref=SUB-1a2b3c4d5e -d at_period_end=1

POST/api/v1/subscriptions/renew

past_due: retries the outstanding invoice. Otherwise: closes the current period now and charges the new one (early renewal).

FieldDescription
refrequired
curl -X POST https://jadepos.store/api/v1/subscriptions/renew -H "Authorization: Bearer jade_..." -d ref=SUB-1a2b3c4d5e
→ {"ok":true,"status":"active","current_period_end":"2026-10-27 09:12:44"}

POST/api/v1/subscriptions/pause & /resume

Pause stops billing until resumed; resume puts it back in the billing queue.

FieldDescription
refrequired
curl -X POST https://jadepos.store/api/v1/subscriptions/pause -H "Authorization: Bearer jade_..." -d ref=SUB-1a2b3c4d5e

POST/api/v1/subscriptions/confirm-payment

Confirm the oldest outstanding invoice on a PayPal/Cash App subscription — the API equivalent of the portal's "Mark paid".

FieldDescription
refrequired
curl -X POST https://jadepos.store/api/v1/subscriptions/confirm-payment -H "Authorization: Bearer jade_..." -d ref=SUB-1a2b3c4d5e
→ {"ok":true,"invoice_ref":"INV-63505a"}

GET/api/v1/invoices

The subscription billing ledger: one row per billing period across every subscription.

FieldDescription
statusoptionaloutstanding (default: pending + failed), paid, void, or empty for all.
pageoptional1-based page number.
curl -H "Authorization: Bearer jade_..." "https://jadepos.store/api/v1/invoices?status=outstanding"
→ {"invoices":[{"ref":"INV-63505a","subscription_ref":"SUB-1a2b3c4d5e",
     "method":"card","customer_name":"Lin","amount_cents":1999,"currency":"USD",
     "status":"failed","attempt_count":1,"due_at":"2026-09-27 09:12:44",
     "created_at":"2026-09-27 09:12:44"}]}

Card validation

Every card is validated before any charge is attempted. A failure answers 402 with a reason and nothing is sent to the processor.

NetworkDetected byLengthCVV
VISA Visa4…13, 16, 193
Mastercard Mastercard51–55, 2221–2720163
DISCOVER Discover6011, 644–649, 6516–193
AMEX American Express34, 37154
JCB JCB3528–358916–193
DINERS Diners Club300–305, 36, 38–3914–193

Plus the Luhn checksum and an expiry check against the current date. Other networks are rejected. (Diners and JCB settle on the Discover network in the US.)

Test cards (test mode)

Any future expiry and matching-length CVV work with these:

VISA4242 4242 4242 4242approved
MC5555 5555 5555 4444approved
DISC6011 1111 1111 1117approved
AMEX3782 822463 10005approved
VISA4000 0000 0000 0002declined

Live card gateway relay

In live mode jadePOS POSTs each card charge as JSON to the HTTPS relay URL configured in Payments (loopback http://127.0.0.1 is also accepted for a relay on the same machine), with your relay key in the body:

{"api_key":"...","reference":"pre-...","amount_cents":1250,"currency":"USD",
 "card_number":"...","exp_month":"12","exp_year":"2030","cvv":"..."}

The relay must answer within ~20 seconds:

{"status":"approved","auth_code":"..."}
{"status":"declined","error":"..."}

A ready-to-run relay for NMI and Authorize.net ships with the project: deploy/card_relay.py (Python stdlib, no installs) with deploy/relay_config.json choosing the processor and holding its credentials — they never enter jadePOS. It ships pointed at NMI's public sandbox until you swap in real keys; for Authorize.net, create free sandbox credentials at developer.authorize.net and set sandbox: true until go-live.

The same endpoint also handles recurring billing's two extra request shapes (put a card on file, then charge it later without ever touching the PAN again):

{"api_key":"...","tokenize":true,"card_number":"...","exp_month":"12","exp_year":"2030","cvv":"...",...}
→ {"status":"tokenized","token":"..."}   (NMI Customer Vault / Authorize.Net CIM)

{"api_key":"...","charge_token":true,"reference":"...","amount_cents":1999,"currency":"USD","token":"..."}
→ {"status":"approved","auth_code":"..."} / {"status":"declined","error":"..."}

Processor webhooks

Point your processor's webhook (Authorize.Net: net.authorize.payment.refund.created, …void.created, …fraud.declined) at https://jadepos.store/webhook/authnet. The relay verifies the X-ANET-Signature HMAC-SHA512 against your Signature Key (Ling has no HMAC builtin, so it must happen in the relay), then forwards the verified event to the till over loopback.

The benefit: money that moves outside the till stays truthful inside it — a refund or void issued from the Authorize.Net dashboard automatically flips the matching order (correlated by the order ref jadePOS sends as the transaction reference), records it on the ledger, and updates the customer's receipt. Delivery is idempotent, so retried webhooks never double-refund.

Status codes

200Success — including recorded declines on wallet endpoints; card declines use 402.
400Malformed request: missing/invalid field, unknown method, bad SKU.
401Missing, revoked, or wrong API key.
402Card charge declined (validation failure or processor decline) — body carries approved:false and error.
404Unknown route.
429Rate limited — slow down and retry.