# MediaGlobe Pay developer integration guide > MediaGlobe Pay is a server-to-server payments API with hosted buyer checkout, persistent payment links, typed checkout resources, Smart Routing, payment status, payout-wallet reads, balance rollups, and signed outbound webhooks. This file is written for coding agents. Treat the OpenAPI document as the machine-readable contract and the human API reference as the explanation of operational behavior. If this file conflicts with either one, follow the OpenAPI document and report the mismatch. ## Primary documentation - [Human API reference](https://mediaglobepay.com/api-docs): Request fields, response examples, scopes, errors, retries, and production guidance. - [OpenAPI 3.1 contract](https://mediaglobepay.com/openapi.json): Authoritative public schema for Merchant API routes and models. - [Developer dashboard](https://dashboard.mgpay.link): Create scoped API keys, configure payout wallets, set Smart Routing, and manage webhooks. - [Shopify integration](https://mediaglobepay.com/docs/shopify): Storefront redirect-v2 setup and launch verification. - [WooCommerce integration](https://mediaglobepay.com/docs/woocommerce): Current plugin requirements and installation. - [OpenCart integration](https://mediaglobepay.com/docs/opencart): Current extension package and setup. - [PrestaShop integration](https://mediaglobepay.com/docs/prestashop): Current module package and setup. - [WHMCS integration](https://mediaglobepay.com/docs/whmcs): Current gateway package and setup. ## Contract summary - Base URL: `https://api.mgpay.link/v1` - Authentication: `Authorization: Bearer ` - Request bodies: JSON with `Content-Type: application/json` - Request correlation: every response includes `X-Request-Id`; JSON bodies include `request_id` at the top level only. Objects nested inside a list `data` array (for example each payment in `GET /payments`) do not repeat `request_id`, so validate it on the envelope, not on items. - API key prefixes: `mgp_live_` and `mgp_test_` - Buyer checkout and tracking URLs are tokenless; use returned URLs exactly as provided - Merchant-owned payment links and checkout registrations persist until explicitly deleted, deactivated, archived, or superseded - Terminal payment success status: `completed` - Recurring billing and subscription intervals are not implemented - The Merchant API is server-first. Do not call it directly from customer browsers ## Security rules for every integration 1. Keep API keys and webhook secrets in server-side secret storage. Never include them in browser JavaScript, mobile bundles, logs, analytics, support screenshots, or URLs. 2. Never expose callback tokens, settlement addresses, organization IDs, internal routing data, or full transaction identifiers to a buyer. 3. Do not treat a browser redirect, return URL, tracking page, or client-side status as payment authority. Reconcile with a verified webhook and, when needed, an authenticated payment read. 4. Verify webhook signatures against the exact raw request body before JSON parsing. Enforce the five-minute timestamp window and compare signatures in constant time. 5. Deduplicate webhook work by `X-MGPay-Delivery-Id`. Return 2xx only after durable processing. 6. Use HTTPS webhook endpoints. Private, local, reserved, redirecting, or credential-bearing URLs are not supported. 7. Treat provider availability, minimums, currencies, and country compatibility as dynamic. Query current data; do not hard-code a permanent provider list. ## API key scopes Create least-privilege keys in Dashboard → Settings → API keys. - `payments:read`: `GET /me`, payment-link reads, payment reads, wallets, providers, and merchant balance - `payments:write`: create and delete payment links - `links:read`: read typed checkouts and the account routing policy - `links:write`: create, update, publish, and deactivate typed checkouts; replace the account routing policy - `webhooks:read`: list webhook endpoints - `webhooks:write`: create, update, test, rotate, and delete webhook endpoints There is no wildcard scope to select in the Dashboard. Request the explicit read and write scopes your application needs. `GET /me` specifically requires `payments:read`, so it is not a valid credential check for a webhook-only or links-only key. Test keys label credential intent but do not create an isolated organization-data sandbox. Production currently has no test-wallet binding, so `POST /payment_links` with an `mgp_test_` key returns `503 test_wallet_base_url_required` (a test key with no payout wallet returns `409 payout_wallet_required` first). There is no sandbox rehearsal path: the only way to exercise the full buyer flow is an authorized low-value live payment, which is why every other step here can be verified with reads and the webhook test event. ## Recommended generic-app flow 1. Configure a valid payout wallet and account Smart Routing policy in the Dashboard. 2. Create a live API key with `payments:read`, `payments:write`, and the webhook scopes your backend needs. 3. Register a signed webhook endpoint and store the revealed `whsec_` secret immediately; it is shown once. 4. Create a persistent payment link with a unique `Idempotency-Key` and a unique `tracking_id`. 5. Send the buyer to the returned `payment_url` exactly as returned. Do not append provider parameters or reconstruct the URL. 6. Use the returned `tracking_url` for customer-visible status. 7. Process verified webhook events idempotently. Use authenticated reads for reconciliation, not as a replacement for signature verification. 8. Delete the payment link when the merchant intentionally retires it. Deletion preserves payment and settlement history. ## Create a live payment link `POST /payment_links` is the simplest payable integration for a custom app. It requires `payments:write`, a live key, a valid payout wallet, and an account Smart Routing policy containing at least one verified provider that supports the link currency; otherwise it returns `409 payout_wallet_required` or `409 routing_policy_required` before any registration. Each link snapshots up to eight of those providers, chosen by the platform curated priority, as its immutable provider ceiling. The account policy remains the maximum ceiling and may hold more providers than any single link uses. Registration-side rejections of `tracking_id`, `currency`, or `amount` return `400 invalid_tracking_id`, `invalid_currency`, or `invalid_amount` with the matching `param`. ```bash curl --request POST 'https://api.mgpay.link/v1/payment_links' \ --header "Authorization: Bearer $MGPAY_API_KEY" \ --header 'Content-Type: application/json' \ --header 'Idempotency-Key: order-6735-attempt-1' \ --data '{ "amount": 49.95, "currency": "USD", "tracking_id": "order_6735_attempt_1", "description": "Order 6735", "success_url": "https://merchant.example/orders/6735/success", "cancel_url": "https://merchant.example/orders/6735" }' ``` Important request rules: - `amount` is required and must be between 1 and 100000. - `currency` may be `USD`, `EUR`, `CAD`, or `GBP`; it defaults to `USD`. - `tracking_id` must match `^[A-Za-z0-9_-]{6,80}$`. - `customer_email`, `business_name`, `description`, and `product_name` are optional. - `success_url` and `cancel_url` are optional absolute http(s) URLs. After the payment is confirmed, the hosted checkout forwards the buyer to `success_url` with `mgpay_tracking_id` and `mgpay_status=completed` appended, and the tracking page offers a "Continue to merchant" action. When `success_url` is set, the payment provider opens in a new tab so the checkout can return the buyer. Confirm the payment server-side with `GET /v1/payments/{id}` or the signed webhook; the redirect itself is not proof of payment. - `checkout_style` may be `page` or `widget`; it defaults to `page`. - The default settlement representation is Polygon USDC. Do not override network or token unless the current public API contract and your account configuration explicitly support the intended flow. Persist at least the returned `id`, `payment_url`, `tracking_id`, `tracking_url`, `status`, and `request_id`. Do not invent decorative resource-ID prefixes; current authenticated resources use UUIDs. ### Idempotency Idempotency applies specifically to payment-link creation. - Reuse both the `Idempotency-Key` and `tracking_id` only when retrying the same logical create, including after a timeout or ambiguous response. - An exact retry returns the original link without creating another wallet session. - Reusing either identity with changed input returns `409 idempotency_payload_mismatch`. - After a link is deleted, its identities remain tombstoned. Retrying them returns `409 payment_link_deleted`; create a new logical link with new identities. - Do not assume general idempotency for checkout drafts, webhook endpoint creation, or other API operations. ## Typed checkout resources Use typed checkout resources when the merchant needs a reusable hosted page, payment-link template, pricing table, or widget managed as draft and live configuration. - `GET /checkouts`: list checkout entities; optional exact `type` and `status` filters (`status` is `draft`, `live`, `deactivated`, or `archived`; any other value returns `400 invalid_filter`) - `POST /checkouts`: create a private draft - `GET /checkouts/{id}`: read draft and live snapshots - `PATCH /checkouts/{id}`: update name, draft config, and/or draft routing - `POST /checkouts/{id}/publish`: atomically publish the current config and routing fingerprint - `POST /checkouts/{id}/deactivate`: deactivate the checkout and registered public slots Update and publish return `409 checkout_archived` for an archived checkout and `409 concurrent_update` when the checkout changed concurrently; retry `concurrent_update`. Checkout types are `hosted_page`, `payment_link`, `pricing_table`, and `widget`. Publishing returns the stable live URL. Use that URL as returned; do not infer a host or path. Important limitation: publishing through the API promotes configuration and routing only. The payable product or pricing-tier registrations behind a checkout are created when the checkout is published from the Dashboard, so an API-only checkout renders a live page with nothing to buy. Use `POST /payment_links` for a fully API-driven payable flow, and use the checkout routes to manage branding and routing of Dashboard-published checkouts. The public buyer bundle keeps only these `config` keys (camelCase or snake_case): `backgroundColor`, `themeColor`, `buttonColor`, `textColor`, `layout` (`minimal`, `standard`, `detailed`), `template` (`default`, `shopify-style`, `stripe-style`, `minimal`), `showLogo`, `showDescription`, `header`, `logo`, `businessName`, `successUrl`, `cancelUrl`, `allowedProviderIds`, `customDomain`. Other keys are stored in the draft but never reach buyers. When sending `routing`, send exactly `version`, `mode`, and `providerIds`; echoing the `versionId` or `versionNumber` from a response returns `400 invalid_routing_config`. Checkout routing configuration version 1 has this shape: ```json { "version": 1, "mode": "inherit", "providerIds": [] } ``` Mode cardinality is exact: - `inherit`: zero provider IDs - `smart`: zero to eight provider IDs - `customer_choice`: one to eight provider IDs - `single`: exactly one provider ID The account routing policy is the hard provider ceiling. A checkout may inherit or narrow that set but cannot add a provider outside it. Draft routing changes do not rewrite an already published attempt; publishing promotes an immutable routing version with the matching config draft. ## Account Smart Routing - `GET /routing`: read configured state, immutable version identity, enabled provider IDs, and config hash - `PATCH /routing`: replace the enabled provider ceiling with `{"enabled_provider_ids":[...]}` Only currently verified lowercase provider IDs are accepted; an unverified or otherwise invalid set returns `400 invalid_provider_ids` with `param` `enabled_provider_ids`. Query `GET /providers` before changing routing. Replacing the account policy affects what future checkout versions may use; it must not be used to rewrite an in-flight payment attempt. ## Payment status and reconciliation - `GET /payment_links`: list persistent links - `GET /payment_links/{id}`: retrieve one link - `DELETE /payment_links/{id}`: retire a link and its dedicated registration while preserving history - `GET /payments`: list normalized payment attempts with cursor pagination - `GET /payments/{id}`: retrieve one normalized payment Payment statuses are: `created`, `awaiting_address`, `awaiting_payment`, `detected`, `confirming`, `completed`, `expired`, `failed`, `refunded`, `cancelled` Only `completed` represents terminal success. There is no `paid` payment status. Do not mark an order complete based on redirect success or a client-side page. List endpoints use `limit` from 1 to 100 and `starting_after` with the opaque `next_cursor` returned by the preceding page. Responses have this shape: ```json { "object": "list", "url": "/v1/payments", "has_more": true, "next_cursor": "opaque-cursor", "data": [], "request_id": "request-id" } ``` ## Wallets, providers, and balance - `GET /wallets`: read non-deleted payout wallets. Wallet changes are Dashboard-managed. - `GET /providers`: read the curated provider identity and current capability snapshot. - `GET /merchant/balance`: read the computed completed-payment and merchant-allocation rollup. The balance response is an application rollup, not a bank or payout-processor ledger. Availability currently uses a 24-hour age rule, `in_transit_balance` is zero, and next-payout fields are estimates. Preserve currencies with all amount values. ## Webhook endpoint lifecycle - `GET /webhook_endpoints` - `POST /webhook_endpoints` - `PATCH /webhook_endpoints/{id}` - `DELETE /webhook_endpoints/{id}` - `POST /webhook_endpoints/{id}/rotate_secret` - `POST /webhook_endpoints/{id}/test` Create example: ```bash curl --request POST 'https://api.mgpay.link/v1/webhook_endpoints' \ --header "Authorization: Bearer $MGPAY_API_KEY" \ --header 'Content-Type: application/json' \ --data '{ "url": "https://merchant.example/webhooks/mediaglobe-pay", "events": ["payment.completed", "payment.failed", "payment.refunded"], "description": "Production order reconciliation", "active": true }' ``` Store the returned `whsec_` secret immediately. Lists never return it. Secret rotation reveals the new secret once and keeps the previous secret available to the delivery worker for a five-minute grace period. ### Webhook request body Production `payment.*` deliveries POST the stored event payload as the entire JSON body. There is no outer `{ "type", "data" }` envelope; the event type is in the `X-MGPay-Event-Type` header. A `payment.completed` body looks like this: ```json { "payment_id": "bcff2b60-72aa-4211-a409-30c27dbad75c", "payment_link_id": "0bb340fb-3c53-48a8-b2e8-2f0a61dc7a94", "tracking_id": "order_6735_attempt_1", "organization_id": "5f51d978-7e6b-4fcf-a2db-5db118abce5b", "status": "completed", "currency": "USD", "amount_expected": 49.95, "amount_received": 49.95, "value_coin": 49.95, "value_usd": 49.95, "expected_usd": 49.95, "settlement_currency": "USD", "coin": "polygon_usdc", "network": "polygon", "txid_in": "0x...", "txid_out": "0x...", "pending": false, "callback_id": 1234 } ``` - `tracking_id` is the value you supplied to `POST /payment_links` (or the generated one returned by it). Match webhooks to your order by `tracking_id`; `payment_link_id` and `payment_id` are the UUIDs returned by the API. `callback_id` is an internal integer and is not stable across environments. - `payment.detected` and `payment.failed` bodies share the shape; `txid_out` is null unless the status is `completed`, and `amount_received` is the coin amount for non-completed events. - `status` is the payment status after this event. Only `completed` means paid. Never grant fulfillment on `detected`, `pending: true`, or the redirect back to your site. - The `POST /webhook_endpoints/{id}/test` body is different and self-describing: `{ "id", "object": "event", "type", "test": true, "endpoint_id", "requested_at", "data": { "object": { ... } } }`. Branch on `test === true` (or the `X-MGPay-Event-Type` header) before touching order state. Production payment flows currently emit `payment.detected`, `payment.completed`, `payment.failed`, `payment.expired`, and `payment.refunded`. The accepted subscription enum also contains event values reserved for tests or narrower platform flows; do not assume every accepted event is generally emitted. ### Verify webhook signatures Relevant headers: - `X-MGPay-Delivery-Id` - `X-MGPay-Event-Id` - `X-MGPay-Event-Type` - `X-MGPay-Timestamp` - `X-MGPay-Signature` - `X-MGPay-Signature-Previous` during rotation grace The signed message is `${timestamp}.${rawBody}`. The signature is lowercase HMAC-SHA256 hex with a `sha256=` prefix. ```js import crypto from 'node:crypto'; export function verifyMediaGlobePayWebhook({ rawBody, headers, secret }) { const timestamp = headers['x-mgpay-timestamp']; const signature = headers['x-mgpay-signature'] || ''; const parsedTimestamp = Number(timestamp); const ageSeconds = Math.abs(Math.floor(Date.now() / 1000) - parsedTimestamp); if (!Number.isInteger(parsedTimestamp) || ageSeconds > 300) { throw new Error('invalid_timestamp'); } const expected = 'sha256=' + crypto .createHmac('sha256', secret) .update(timestamp + '.' + rawBody) .digest('hex'); const actualBytes = Buffer.from(signature); const expectedBytes = Buffer.from(expected); if (actualBytes.length !== expectedBytes.length || !crypto.timingSafeEqual(actualBytes, expectedBytes)) { throw new Error('invalid_signature'); } return JSON.parse(rawBody); } ``` Capture the raw request bytes before any JSON body parser transforms them. After signature verification, deduplicate by delivery ID, validate the event type, apply the state change in a database transaction, and then return 2xx. ### Delivery behavior - Request timeout: 10 seconds - New delivery attempt budget: 36 - Retry strategy: exponential backoff capped at six hours - Success: any 2xx response - Terminal failure: any redirect or HTTP 410 - Retryable failure: other non-2xx responses, network errors, and timeouts - Endpoint auto-deactivation: after five consecutive failed deliveries where the latest is permanent (a 410 or a redirect), the endpoint's `active` flag is set to false and nothing more is delivered until you PATCH it back to `"active": true` Do not redirect webhook URLs (a deploy that answers 410 or 3xx can deactivate your endpoint). Monitor failed and dead-lettered deliveries. ## Public buyer-facing endpoints (no API key) - `GET https://mgpay.link/api/status.php?payment=` returns `{ status, paid, amount, currency, network, coin, confirmations, timestamp, payment_id, message, source }` and, once `completed`, `success_url` when the checkout has one. It is CORS-restricted to MediaGlobe Pay origins and rate limited, so use it for your own server-side polling only as a convenience; it is not a substitute for the signed webhook. - `GET https://mgpay.link/api/providers` returns the curated provider catalog filtered to the caller's country with each provider's live minimum amount and currency. Prices below every eligible provider's minimum cannot be paid; the Dashboard checkout editor warns about this, and a Single-provider checkout whose preferred provider cannot serve the price asks the buyer to pick a fallback. ## Embed a published checkout on your own page Widgets and pricing tables created in the Dashboard (or through `POST /checkouts` with `type` `widget` or `pricing_table`, then published) render on any page with one script tag. Buttons open the hosted checkout in a new tab with `rel="noopener noreferrer"`; add `data-mgpay-target="self"` to open it in the same tab. ```html ``` The snippet always renders the currently published version. Nothing in it is secret. ## End-to-end example for a custom app (Node.js) ```js import crypto from 'node:crypto'; import express from 'express'; const API = 'https://api.mgpay.link/v1'; const KEY = process.env.MGPAY_API_KEY; // mgp_live_..., server-side only const WEBHOOK_SECRET = process.env.MGPAY_WEBHOOK_SECRET; // whsec_..., shown once at creation // 1. Create a payment link when the order is placed and send the buyer to payment_url. export async function createPaymentLink(order) { const trackingId = `order_${order.id}_attempt_${order.attempt}`; // ^[A-Za-z0-9_-]{6,80}$ const res = await fetch(`${API}/payment_links`, { method: 'POST', headers: { Authorization: `Bearer ${KEY}`, 'Content-Type': 'application/json', 'Idempotency-Key': trackingId, }, body: JSON.stringify({ amount: order.total, // 1 to 100000 currency: 'USD', // USD, EUR, CAD, GBP tracking_id: trackingId, description: `Order ${order.id}`, success_url: `https://merchant.example/orders/${order.id}/thanks`, cancel_url: `https://merchant.example/orders/${order.id}`, }), }); const body = await res.json(); if (!res.ok) throw new Error(`${body.error.code}: ${body.error.message} (${body.error.request_id})`); await db.orders.update(order.id, { mgpayLinkId: body.id, mgpayTrackingId: body.tracking_id, mgpayTrackingUrl: body.tracking_url }); return body.payment_url; // redirect the buyer here, unchanged } // 2. Receive webhooks with the raw body available for signature verification. const app = express(); app.post('/webhooks/mediaglobe-pay', express.raw({ type: '*/*' }), async (req, res) => { const rawBody = req.body.toString('utf8'); const timestamp = req.get('x-mgpay-timestamp'); const signature = req.get('x-mgpay-signature') || ''; const parsed = Number(timestamp); if (!Number.isInteger(parsed) || Math.abs(Math.floor(Date.now() / 1000) - parsed) > 300) return res.status(400).end(); const expected = 'sha256=' + crypto.createHmac('sha256', WEBHOOK_SECRET).update(`${timestamp}.${rawBody}`).digest('hex'); if (expected.length !== signature.length || !crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature))) return res.status(401).end(); const deliveryId = req.get('x-mgpay-delivery-id'); if (await db.webhookDeliveries.exists(deliveryId)) return res.status(200).end(); // duplicate const event = JSON.parse(rawBody); if (event.test === true) { await db.webhookDeliveries.record(deliveryId); return res.status(200).end(); } if (req.get('x-mgpay-event-type') === 'payment.completed' && event.status === 'completed') { await db.transaction(async (tx) => { const order = await tx.orders.findByTrackingId(event.tracking_id); if (order && !order.paidAt) await tx.orders.markPaid(order.id, { paymentId: event.payment_id, txidOut: event.txid_out }); await tx.webhookDeliveries.record(deliveryId); }); } else { await db.webhookDeliveries.record(deliveryId); } res.status(200).end(); }); // 3. Reconcile on demand (support tooling, cron) with an authenticated read. export async function reconcile(paymentId) { const res = await fetch(`${API}/payments/${paymentId}`, { headers: { Authorization: `Bearer ${KEY}` } }); const payment = await res.json(); return payment.status === 'completed'; } ``` The buyer lands on `success_url` with `mgpay_tracking_id` and `mgpay_status=completed` appended; treat that page as a thank-you page that reads your own order state, never as payment proof. The forward happens from the MediaGlobe Pay checkout tab that stays open while the provider tab completes; if the buyer closes it, `success_url` is never visited, so your order completion must be driven by the webhook (or an authenticated read), not by that page. ## Errors, rate limits, and retries Errors use this envelope: ```json { "error": { "type": "invalid_request_error", "code": "missing_required_param", "message": "Human-readable explanation", "param": "amount", "request_id": "request-id" } } ``` Common statuses: - `400`: malformed JSON, invalid cursor, amount, event, checkout type, or parameter; includes `invalid_filter`, `invalid_provider_ids`, `invalid_tracking_id`, `invalid_currency`, and `invalid_amount` - `401`: missing, malformed, revoked, expired, or environment-mismatched key - `403`: missing scope or denied browser origin - `404`: resource, path, or method not found - `409`: conflicting resource state or idempotency identity, including `payout_wallet_required`, `routing_policy_required`, `checkout_archived`, and `concurrent_update` (retry `concurrent_update`) - `415`: wrong request content type - `429`: rate limited; honor `Retry-After` - `502`: a platform dependency answered but not usefully (`wallet_registration_failed`, `routing_snapshot_invalid`, `auth_backend_unavailable`); retry a payment-link create with the identical `Idempotency-Key` and `tracking_id` - `503`: required platform dependency or test-wallet binding unavailable - `504`: `wallet_registration_timeout`; the link may or may not exist. Retry only with the identical `Idempotency-Key` and `tracking_id`; new identities can create a second link Rate-limit checks currently use IP-and-route and API-key-and-route buckets. On `429`, inspect `Retry-After`, `X-RateLimit-Limit`, and `X-RateLimit-Reset`. Retry only safe reads or operations protected by the documented payment-link idempotency contract. Never blindly retry a provider handoff or an unprotected create. ## Commerce integration choices - Custom application: use payment links, signed webhooks, and authenticated reconciliation. - Shopify: follow the redirect-v2 guide. Storefront JavaScript initializes a MediaGlobe Pay hosted checkout; it must never contain a merchant API key. Redirect v2 supports one-time USD carts with at most 100 lines. Subscription and selling-plan carts stay on Shopify's native path. Physical or tax-dependent carts must wait for Shopify's authoritative address, delivery, discount, tax, and final-total data before becoming payable. - WooCommerce: use the current signed-event and authenticated-reconciliation plugin documented in the WooCommerce guide. - OpenCart, PrestaShop, or WHMCS: install only the current package linked from its public guide and verify the displayed SHA-256 checksum before uploading. Commerce-module callbacks are private per-order or per-invoice callbacks. They are not the same as outbound merchant POST webhooks managed through `/webhook_endpoints`. ## Production readiness checklist for coding agents - Confirm the requested product flow is one-time payment, not recurring billing. - Load and validate the current OpenAPI document before generating a client. - Keep API calls and secrets server-side. - Request only the scopes needed by the implemented routes. - Configure a valid payout wallet and Smart Routing policy before creating a live payment link. - Use unique payment-link idempotency and tracking identities. - Store returned buyer URLs exactly; do not synthesize them. - Implement raw-body signature verification, timestamp validation, constant-time comparison, and delivery-ID deduplication. - Match webhook events to orders by `tracking_id`; branch on `test === true` for test deliveries. - Price products at or above the minimums of the providers you enable (`GET https://mgpay.link/api/providers`). - Treat `completed` as the only terminal payment success state. - Implement cursor pagination and preserve `request_id` in operational logs. - Honor `Retry-After`; retry only operations that are safe under their documented idempotency contract. - Run a controlled low-value live payment only with merchant authorization. - Reconcile the controlled payment through both a verified webhook and an authenticated API read. - Do not claim production readiness from a successful build, redirect, or health check alone. ## Optional reading - [MediaGlobe Pay home](https://mediaglobepay.com/): Product overview and merchant application. - [Privacy policy](https://mediaglobepay.com/privacy): Public privacy terms. - [Terms of service](https://mediaglobepay.com/terms): Public service terms.