Merchant docs only

DGEN Payments API Reference

Store-scoped API docs for checkout orders, Stripe Auto-pay and subscriptions, products, payment links, wallets, admin-enabled store mailing, GCash Android automation, public checkout helpers, and signed outbound webhooks.

AuthStore-scoped API keys
ScopeMerchant endpoints only
WebhooksSigned delivery payloads
GCashAndroid app forwarder

Endpoint groups

Jump to the part you need.

These docs intentionally exclude internal platform, cron, global, and provider-webhook endpoints. This page only lists endpoints a merchant integration or customer checkout can use.

Authentication API KEY

All /api/v1/* endpoints require Authorization: Bearer pdgen_live_.... Use Idempotency-Key on create endpoints when retrying from your backend.

curl -H 'Authorization: Bearer pdgen_live_xxx' https://payments.decentralizedgen.com/api/v1/store

Store API v1 - store API KEY

EndpointUsed forExample
GET/api/v1/storeConfirm auth and read store status, provider toggles, payout currency, and owner profile.
{ "id":"store_...", "status":"approved",
  "providers": { "stripe_enabled":true, "gcash_enabled":true },
  "owner": { "id":"usr_...", "email":"owner@example.com" } }

Store API v1 - orders API KEY

EndpointUsed forExample
POST/api/v1/ordersCreate a one-shot checkout order. Pass a product id or a custom amount. payment_method may be card, upi, or gcash and is added to checkout_url. Supports Idempotency-Key, metadata, return/cancel URLs, and custom expiry.
{ "currency":"USD", "amount_minor":1999,
  "payment_method":"upi",
  "customer": { "email":"a@b.com", "name":"Alice", "phone":"+639..." },
  "success_url":"https://merchant.example/paid",
  "metadata": { "reference":"invoice_123" } }
GET/api/v1/ordersList orders. Filters: status, customer_email, product_id, payment_link_id, provider, from, to, limit.
{ "data":[{ "id":"ord_...", "status":"paid", "provider":"gcash" }], "limit":50 }
GET/api/v1/orders/:idRead one order including customer, product, link, fees, metadata, GCash, UPI, Stripe, and refunds.
{ "id":"ord_...", "provider":"gcash", "gcash": { "paid_minor":11248 } }
POST/api/v1/orders/:id/cancelCancel a pending order.
{ "ok": true }
POST/api/v1/orders/:id/refundRefund all or part of a paid Stripe order. GCash is not refunded through this API because money goes to your own GCash account.
{ "amount_minor":500, "reason":"duplicate" }

Store API v1 - products API KEY

EndpointUsed forExample
GET/api/v1/productsList products. Use ?archived=1 to include archived products.
{ "data":[{ "id":"prd_...", "name":"VIP", "prices":[...] }] }
POST/api/v1/productsCreate a product and auto-generate its permanent payment link.
{ "name":"VIP", "base_currency":"USD", "base_amount_minor":1999 }
GET/api/v1/products/:idRead one product. If its default link is missing, DGEN self-heals it.
{ "id":"prd_...", "payment_link": { "checkout_url":"https://..." } }
PATCH/api/v1/products/:idUpdate name, description, and price table.
{ "name":"VIP v2" }
POST/api/v1/products/:id/payment-linkGet or create the product's default permanent payment link.
{ "id":"plk_...", "created":false, "checkout_url":"https://..." }

Store API v1 - user wallets API KEY

EndpointUsed forExample
POST/api/v1/walletsCreate a per-customer wallet. Requires at least one of email, phone, or Discord id.
{ "name":"Juan", "email":"juan@example.com", "phone":"+639..." }
GET/api/v1/walletsList wallets for the store.
{ "data":[{ "id":"uwl_...", "gcash_balance_minor":0 }] }
GET/api/v1/wallets/:idRead one wallet.
{ "id":"uwl_...", "topup_link_id":"plk_..." }
GET/api/v1/wallets/:id/balanceRead Stripe and GCash wallet balances plus top-up checkout URL.
{ "stripe": { "balance_minor":5000 }, "gcash": { "balance_minor":0 } }
GET/api/v1/wallets/:id/ledgerNewest-first wallet ledger entries.
{ "data":[{ "kind":"topup_gcash", "delta_minor":1999 }] }

Store API v1 - Stripe auto-pay SCOPED API KEY

The store owner must approve the store, enable Stripe, accept the Auto-pay terms, and enable Auto-pay in DGEN first. Use a least-privilege scoped API key. Every mutation requires Idempotency-Key (8-120 printable ASCII characters); a same-key/same-input retry replays the result for 24 hours, while changed input returns HTTP 409. Payment data is collected directly by Stripe and DGEN never receives or stores PAN/CVC.

Supported payment methods
Card is the currently enabled and certified reusable Auto-pay method. Eligible Apple Pay, Google Pay, and Link experiences may be presented by Stripe as card-backed wallets depending on the customer's browser, device, country, and Stripe account configuration. Do not assume they will appear. GCash, UPI QR/UTR, crypto, BNPL, manual bank transfer, and other one-time methods are not Auto-pay methods. Bank debit and other mandate rails are not exposed by this API yet.

Required headers
Authorization: Bearer <store API key>
Content-Type: application/json
Idempotency-Key: <8-120 printable ASCII characters>   # every POST, PATCH, DELETE

Auto-pay scopes
GET /autopay                                      subscriptions:read
GET/POST/DELETE /return-origins                   subscriptions:read / subscriptions:write
GET/POST/PATCH/DELETE /subscription-plans         plans:read / plans:write
GET/POST/PATCH/DELETE /subscription-links         subscriptions:read / subscriptions:write
GET/POST/DELETE /billing-agreements               agreements:read / agreements:write
POST /billing-agreements/:id/charges              charges:write
GET /recurring-charges/:id                        subscriptions:read
GET/POST subscription resources                   subscriptions:read / subscriptions:write

Return URL allowlist
Register every external billing origin before using it as a setup return_url or subscription-link success_url/cancel_url. DGEN normalizes an HTTPS URL to its origin (scheme + hostname + optional non-default port), so paths, query strings, and fragments are not stored. Paymenter at billing.sereinhost.com must register https://billing.sereinhost.com. Registration expands only the allowed redirect origins; each submitted return URL is still checked against the store website, DGEN, or this explicit allowlist.

DGEN-managed subscription flow
1. GET /autopay and require accept_new_subscriptions=true.
2. POST /subscription-plans, or select an active existing plan.
3. POST /subscription-links with collection_mode=dgen_managed. For a billing platform, bind customer.external_reference and subscription_external_reference in this authenticated request, then redirect the customer to checkout_url.
4. Stripe Checkout collects the first payment, saves the reusable method, and creates the Stripe Billing subscription. DGEN creates the customer, agreement, and local subscription records.
5. Fulfil only from signed subscription.payment_succeeded/order.paid events or confirmed API state—not from a browser redirect.

The separate /billing-agreements/setup-sessions then /subscriptions flow remains available when a card must be linked before the plan is selected.

External-scheduler flow
Create a subscription link with collection_mode=external_scheduler and, when integrating a billing platform, bind the trusted customer.external_reference and subscription_external_reference during link creation. The public hosted form can submit customer email/name but can never choose either external reference. Its Checkout collects the first plan payment and saves the method with explicit off-session consent. After billing_agreement.active, correlate using data.customer_external_reference, then POST /billing-agreements/:id/charges once per later invoice using a unique Idempotency-Key and external_reference. HTTP 202 means accepted, not paid. Read GET /recurring-charges/:id and consume signed payment webhooks for the final result. Never run this mode and a DGEN-managed subscription for the same billing schedule.

Pagination and errors
List endpoints accept limit=1..100 and an opaque next_cursor returned by the previous page. Subscription lists also accept status and plan_id. Error bodies are JSON: { "error":"safe message", "code":"STABLE_CODE" }. Common statuses: 400 validation/idempotency header, 401 invalid key, 403 scope/store/Auto-pay restriction, 404 tenant-scoped resource not found, 409 state or idempotency conflict, 410 expired setup session, 402 failed off-session charge, 429 rate limit, and 5xx provider/service failure.
EndpointUsed forExample
GET/api/v1/autopayRead store auto-pay and renewal-suspension state.
{ "accept_new_subscriptions":true, "renewals_suspended":false }
GET/api/v1/return-originsList additional allowlisted redirect origins. Scope: subscriptions:read.
{ "data":[{ "origin":"https://billing.sereinhost.com", "label":"Paymenter" }] }
POST/api/v1/return-originsAllowlist a normalized HTTPS origin for setup/link returns. Requires Idempotency-Key. Scope: subscriptions:write.
{ "origin":"https://billing.sereinhost.com/account", "label":"Paymenter" }
DELETE/api/v1/return-originsRemove an additional origin from future return-URL validation. Requires Idempotency-Key. Scope: subscriptions:write.
{ "origin":"https://billing.sereinhost.com" }
POST/api/v1/subscription-plansCreate an immutable recurring Stripe Product/Price mirror. Scope: plans:write.
{ "name":"Pro", "currency":"USD", "amount_minor":1999, "interval":"month", "interval_count":1 }
GET/api/v1/subscription-plansList plans. Scope: plans:read.
{ "data":[{ "id":"spl_...", "name":"Pro" }] }
GET/api/v1/subscription-plans/:idRead one tenant-scoped plan. Scope: plans:read.
{ "id":"spl_...", "currency":"USD", "amount_minor":1999, "interval":"month", "active":true }
PATCH/api/v1/subscription-plans/:idUpdate display fields or active state; price/currency/interval remain immutable. Scope: plans:write.
{ "name":"Pro (new name)", "active":true }
DELETE/api/v1/subscription-plans/:idArchive a plan without deleting subscription history. Scope: plans:write.
{ "id":"spl_...", "archived":true }
POST/api/v1/subscription-linksCreate a reusable hosted subscription link for DGEN-managed renewal or an external scheduler. Optionally bind trusted customer and subscription references here; the public form cannot set them. Scope: subscriptions:write.
{ "plan_id":"spl_...", "collection_mode":"external_scheduler", "customer":{ "external_reference":"paymenter_user_42", "email":"a@b.com", "name":"Alice" }, "subscription_external_reference":"paymenter_service_84", "success_url":"https://merchant.example/billing/success", "max_uses":1 }
GET/api/v1/subscription-linksList hosted subscription links and their checkout_url, mode, active state, and usage. Scope: subscriptions:read.
{ "data":[{ "id":"slnk_...", "collection_mode":"dgen_managed", "checkout_url":"https://payments.decentralizedgen.com/subscribe/..." }] }
PATCH/api/v1/subscription-links/:idUpdate description, usage limit, or active state. Plan and collection mode are immutable. Scope: subscriptions:write.
{ "active":false }
DELETE/api/v1/subscription-links/:idDisable future checkout sessions without affecting existing subscriptions or agreements. Scope: subscriptions:write.
{ "id":"slnk_...", "active":false }
POST/api/v1/billing-agreements/setup-sessionsCreate a single-use hosted card authorization URL. return_url must share the store website or DGEN origin. Scope: agreements:write.
{ "customer":{ "external_reference":"customer_123", "email":"a@b.com", "name":"Alice" }, "return_url":"https://merchant.example/billing/complete" }
GET/api/v1/billing-agreementsList safe payment-method metadata; Stripe identifiers are never returned. Scope: agreements:read.
{ "data":[{ "id":"agr_...", "status":"active", "brand":"visa", "last4":"4242" }] }
GET/api/v1/billing-agreements/:idRead one agreement and safe display metadata. Scope: agreements:read.
{ "id":"agr_...", "status":"active", "method_type":"card", "display_name":"Visa ending 4242" }
DELETE/api/v1/billing-agreements/:idRevoke future use. Active subscriptions must first be canceled or moved. Scope: agreements:write.
{ "id":"agr_...", "status":"revoked" }
POST/api/v1/subscriptionsCreate a DGEN-managed Stripe Billing subscription. Scope: subscriptions:write.
{ "plan_id":"spl_...", "billing_agreement_id":"agr_...", "external_reference":"service_123", "quantity":1 }
GET/api/v1/subscriptionsList customer subscriptions; filter by status or plan_id. Scope: subscriptions:read.
{ "data":[{ "id":"sub_...", "status":"active" }] }
GET/api/v1/subscriptions/:idRead one subscription with safe customer, plan, and payment display data. Scope: subscriptions:read.
{ "id":"sub_...", "status":"active", "current_period_end":1788172800 }
GET/api/v1/subscriptions/:id/cyclesList invoice/renewal cycles and retry state. Scope: subscriptions:read.
{ "data":[{ "id":"cyc_...", "status":"paid", "attempt_count":1 }] }
GET/api/v1/subscriptions/:id/eventsList the append-only subscription event timeline. Scope: subscriptions:read.
{ "data":[{ "event_type":"subscription.active", "source":"stripe_webhook" }] }
POST/api/v1/subscriptions/:id/pausePause collection for a managed subscription. Scope: subscriptions:write.
{ "id":"sub_...", "status":"paused" }
POST/api/v1/subscriptions/:id/resumeResume a paused subscription. Scope: subscriptions:write.
{ "id":"sub_...", "status":"active" }
POST/api/v1/subscriptions/:id/cancelCancel immediately or at period end.
{ "when":"period_end" }
POST/api/v1/subscriptions/:id/change-planMove a subscription to another immutable plan with explicit proration behavior.
{ "plan_id":"spl_...", "proration_behavior":"create_prorations" }
POST/api/v1/subscriptions/:id/retryRetry the latest eligible failed/open invoice. Scope: subscriptions:write.
{ "subscription_id":"sub_...", "invoice_status":"paid", "accepted":true }
POST/api/v1/subscriptions/:id/payment-method-sessionCreate a hosted card replacement session using a registered return_url. Scope: subscriptions:write.
{ "return_url":"https://merchant.example/billing/complete" }
POST/api/v1/billing-agreements/:id/chargesExternal-scheduler mode: request one confirmed off-session charge. Scope: charges:write.
{ "amount_minor":1999, "currency":"USD", "external_reference":"invoice_123" }
GET/api/v1/recurring-charges/:idRead asynchronous off-session charge status.
{ "id":"rca_...", "status":"processing", "order_id":"ord_..." }

Store API v1 - mailing API KEY

Send one email through the DGEN mail template after an administrator enables Mailing for the store. The store name is shown as the sender name; the actual From address remains the verified DGEN mailbox. Dashboard and API sends share two rolling 24-hour limits for that store: 10 pending or sent messages per normalized recipient and 100 total across all recipients. Failed sends release both quota slots.

Required headers
Authorization: Bearer <store API key>
Idempotency-Key: <required unique key>
Content-Type: application/json

Body parameters
to       required valid email address
subject  required string, 1–160 characters
html     required string, maximum 32 KiB; server-allowlisted
css      optional string, maximum 8 KiB; server-allowlisted

cURL request
curl -X POST 'https://payments.decentralizedgen.com/api/v1/mailing/send' \
  -H 'Authorization: Bearer pdgen_live_xxx' \
  -H 'Idempotency-Key: mailing-order-123' \
  -H 'Content-Type: application/json' \
  -d '{"to":"customer@example.com","subject":"Order update","html":"<p>Hello <strong>Alice</strong>.</p>","css":".store-mail-content strong { color: #61d83c; }"}'

Request body
{
  "to": "customer@example.com",
  "subject": "Order update",
  "html": "<p>Hello <strong>Alice</strong>.</p>",
  "css": ".store-mail-content strong { color: #61d83c; }"
}

HTTP 201
{
  "message": {
    "id": "mail_...",
    "status": "sent",
    "source": "api",
    "to": "customer@example.com",
    "subject": "Order update",
    "sender": {
      "name": "My Store",
      "address": "payments@decentralizedgen.com"
    },
    "created_at": 1785500000,
    "sent_at": 1785500001
  },
  "quota": {
    "limit": 100,
    "used": 1,
    "remaining": 99,
    "window_seconds": 86400,
    "resets_at": 1785586400,
    "recipient": {
      "email": "customer@example.com",
      "limit": 10,
      "used": 1,
      "remaining": 9,
      "window_seconds": 86400,
      "resets_at": 1785586400
    }
  }
}

Errors
403 MAILING_NOT_ENABLED  An administrator has not enabled Mailing for this store.
409                      Idempotency-Key was reused with conflicting input.
429 MAILING_QUOTA_EXCEEDED  quota_scope is recipient after 10 messages to one address, or total after 100 messages from the store.
502 / 503                Mail provider or configuration failure.
EndpointUsed forExample
POST/api/v1/mailing/sendSend one allowlisted HTML email with optional custom CSS inside the DGEN mail template. Exactly one recipient is accepted per request.
{ "to":"customer@example.com",
  "subject":"Order update",
  "html":"<p>Hello <strong>Alice</strong>.</p>",
  "css":".store-mail-content strong { color:#61d83c; }" }

GCash Android app + notifications API KEY

The DGEN GCash Android app uses these endpoints after the merchant enters a Store API key and grants notification/background permissions.

EndpointUsed forExample
POST/api/v1/gcash/heartbeatDevice heartbeat. Upserts the device row by store and hardware id.
{ "hwid":"abc123", "phone_number":"+639...",
  "device_model":"Pixel", "os_version":"Android",
  "app_version":"1.0.0" }
POST/api/v1/gcash/notificationsForward a receive notification. DGEN dedupes by dedupe_id, then matches exact amount or partial amount by phone.
{ "full_notification":"You received PHP 499.00...",
  "sender":"GCash", "from_number":"0917...", "amount":499,
  "reference_no":"0041073634750", "dedupe_id":"gcash-ref-0041073634750",
  "hwid":"abc123", "device_phone":"+639..." }
GET/api/v1/gcash/store-infoCompatibility smoke test for the Android app. Redirects to /api/v1/store.
301 redirect
GET/api/v1/gcash/notificationsRead recent inbound GCash notifications. Filter by match_type=exact|partial|unmatched.
{ "data":[{ "id":"gcn_...", "match_type":"exact" }] }
GET/api/v1/gcash/notifications/:idRead one notification row.
{ "id":"gcn_...", "reference_no":"004107..." }

Public checkout PUBLIC

EndpointUsed forExample
GET/api/checkout/:tokenHydrate hosted checkout page state.
{ "kind":"order", "status":"pending", "available_providers":{...} }
GET/api/checkout/:token/quote?currency=EURCurrency quote for checkout currency selection.
{ "currency":"EUR", "amount_minor":1830, "fx_rate":0.929 }
POST/api/checkout/:token/customerSubmit customer details for a one-shot order or permanent link visit.
{ "email":"a@b.com", "name":"Alice", "phone":"+639..." }
POST/api/checkout/:token/select-providerChoose stripe, crypto, gcash, or upi where available.
{ "provider":"gcash" }
POST/api/checkout/:token/intentCreate/reuse Stripe PaymentIntent.
{ "client_secret":"pi_..._secret_..." }
GET/api/checkout/:token/statusPoll paid/partial/failed status.
{ "status":"partial", "remaining_minor":5000 }
POST/api/checkout/:token/gcash-phoneSave customer GCash phone for SMS matching.
{ "phone":"+639..." }
POST/api/checkout/:token/gcash-reconcileCheckout-side reconcile helper for GCash pending/partial orders.
{ "ok": true }
POST/api/checkout/:token/upi-utrSubmit UPI UTR/reference for checkout-side tracking.
{ "utr":"123456789012" }
POST/api/checkout/:token/wallet-payPay from a customer wallet balance where available.
{ "wallet_id":"uwl_...", "balance":"gcash" }
POST/api/checkout/:token/reset-customerClear customer details and provider.
{ "ok": true }
POST/api/checkout/:token/reset-providerClear selected provider only.
{ "ok": true }
POST/api/checkout/:token/failMark expired checkout as failed after countdown.
{ "ok": true, "status":"failed" }

Outbound webhook payloads SIG

Configured merchant webhook URLs receive X-DGEN-Signature: t=<unix>,v1=<hmac>.

EndpointUsed forExample
order.paidStripe, GCash, UPI, or wallet order captured. Includes metadata and top-level reference.
{ "event":"order.paid", "data":{ "order_id":"ord_...", "reference":"invoice_123" } }
order.refundedRefund total increased on a Stripe order.
{ "event":"order.refunded", "data":{ "order_id":"ord_...", "refunded_delta_minor":500 } }
order.failedCheckout expired or failed.
{ "event":"order.failed", "data":{ "order_id":"ord_...", "reason":"expired" } }
payout.sentA merchant payout request was marked sent.
{ "event":"payout.sent", "data":{ "payout_id":"pay_...", "amount_minor":50000 } }
billing_agreement.active | billing_agreement.revokedA reusable payment agreement became usable or was revoked. customer_external_reference echoes the trusted merchant reference for correlation.
{ "event":"billing_agreement.active", "ts":1785500000, "data":{ "billing_agreement_id":"agr_...", "customer_id":"cus_...", "customer_external_reference":"paymenter_user_42" } }
subscription.created | active | updated | paused | resumed | canceling | canceledManaged subscription lifecycle changed. Treat delivery as at-least-once and fetch current state when ordering matters.
{ "event":"subscription.active", "ts":1785500000, "data":{ "subscription_id":"sub_...", "status":"active" } }
subscription.payment_processing | succeeded | failed | action_requiredA managed subscription invoice/payment changed state. Provision only from succeeded/current confirmed state.
{ "event":"subscription.payment_succeeded", "ts":1785500000, "data":{ "subscription_id":"sub_...", "order_id":"ord_...", "cycle_id":"cyc_..." } }