{
  "openapi": "3.1.0",
  "info": {
    "title": "MediaGlobe Pay Merchant API",
    "version": "2026-09-01",
    "description": "The MediaGlobe Pay server-to-server contract. Customer-facing checkout and tracking URLs are tokenless; never expose API keys, IPN tokens, settlement addresses, or internal organization identifiers in browser code. Merchant API payment links snapshot up to eight providers from the account Smart Routing policy and return a direct buyer handoff URL. links:read and links:write keys cover typed checkout and routing management."
  },
  "servers": [
    {
      "url": "https://api.mgpay.link/v1",
      "description": "Production merchant API"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    { "name": "Merchant" },
    { "name": "Payment links" },
    { "name": "Checkouts", "description": "Typed checkout draft, routing, publish, and lifecycle management with links:* scopes." },
    { "name": "Routing" },
    { "name": "Payments" },
    { "name": "Wallets" },
    { "name": "Providers" },
    { "name": "Balance" },
    { "name": "Webhook endpoints" }
  ],
  "paths": {
    "/me": {
      "get": {
        "tags": ["Merchant"],
        "summary": "Retrieve the current merchant",
        "description": "Requires payments:read. Returns the organization plus the current API-key environment and scopes.",
        "operationId": "retrieveMerchant",
        "responses": {
          "200": {
            "description": "Merchant context",
            "content": {
              "application/json": {
                "schema": { "allOf": [{ "$ref": "#/components/schemas/Merchant" }, { "$ref": "#/components/schemas/RequestIdEnvelope" }] }
              }
            }
          },
          "default": { "$ref": "#/components/responses/ApiError" }
        }
      }
    },
    "/payment_links": {
      "get": {
        "tags": ["Payment links"],
        "summary": "List payment links",
        "description": "Requires payments:read.",
        "operationId": "listPaymentLinks",
        "parameters": [
          { "$ref": "#/components/parameters/Limit" },
          { "$ref": "#/components/parameters/StartingAfter" }
        ],
        "responses": {
          "200": {
            "description": "Cursor page",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ListEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": { "$ref": "#/components/schemas/PaymentLink" }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "default": { "$ref": "#/components/responses/ApiError" }
        }
      },
      "post": {
        "tags": ["Payment links"],
        "summary": "Create a live payment link",
        "description": "Requires payments:write, a valid payout wallet, and an account Smart Routing policy containing at least one verified provider that supports the link currency; otherwise creation 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. Use an mgp_live_ key. Each fresh link receives its own atomic live checkout registration and persists until explicitly deleted; opening payment_url immediately routes the buyer to the eligible on-ramp without a MediaGlobe Pay provider-selection step. Registration-side rejections of tracking_id, currency, or amount return 400 invalid_tracking_id, invalid_currency, or invalid_amount with the matching param. Production has no isolated test-wallet binding, so mgp_test_ creation returns 503 test_wallet_base_url_required. Reuse both Idempotency-Key and tracking_id only when retrying the same logical create, including after an ambiguous response. An exact retry returns the original link without another wallet call. Reusing either identity with changed parameters returns 409 idempotency_payload_mismatch. Once that link is deleted, its identity remains a tombstone: retrying it returns 409 payment_link_deleted before another wallet is created, and a new logical link requires a new idempotency key and tracking ID.",
        "operationId": "createPaymentLink",
        "parameters": [
          { "$ref": "#/components/parameters/IdempotencyKey" }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/PaymentLinkCreate" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created or idempotently replayed payment link",
            "content": {
              "application/json": {
                "schema": { "allOf": [{ "$ref": "#/components/schemas/PaymentLink" }, { "$ref": "#/components/schemas/RequestIdEnvelope" }] }
              }
            }
          },
          "400": {
            "description": "Invalid amount, currency, or tracking_id, including registration-side rejections re-emitted as invalid_tracking_id, invalid_currency, or invalid_amount.",
            "$ref": "#/components/responses/ApiError"
          },
          "409": {
            "description": "payout_wallet_required or routing_policy_required when the account is not ready for the link currency; idempotency_payload_mismatch when the idempotency key or tracking ID was reused with changed payment-link parameters; payment_link_deleted when it belongs to a deleted payment link. Use the original exact parameters for a retry or fresh identity values for a new logical link.",
            "$ref": "#/components/responses/ApiError"
          },
          "502": { "description": "wallet_registration_failed, routing_snapshot_invalid, or auth_backend_unavailable: retry with the identical Idempotency-Key and tracking_id.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
          "503": { "description": "test_wallet_base_url_required for mgp_test_ keys, or a dependency outage.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
          "504": { "description": "wallet_registration_timeout: the link may or may not exist; retry only with the identical Idempotency-Key and tracking_id.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ErrorEnvelope" } } } },
          "default": { "$ref": "#/components/responses/ApiError" }
        }
      }
    },
    "/payment_links/{id}": {
      "get": {
        "tags": ["Payment links"],
        "summary": "Retrieve a payment link",
        "description": "Requires payments:read.",
        "operationId": "retrievePaymentLink",
        "parameters": [{ "$ref": "#/components/parameters/ResourceId" }],
        "responses": {
          "200": {
            "description": "Payment link",
            "content": {
              "application/json": {
                "schema": { "allOf": [{ "$ref": "#/components/schemas/PaymentLink" }, { "$ref": "#/components/schemas/RequestIdEnvelope" }] }
              }
            }
          },
          "default": { "$ref": "#/components/responses/ApiError" }
        }
      },
      "delete": {
        "tags": ["Payment links"],
        "summary": "Delete a payment link",
        "description": "Requires payments:write. Atomically deactivates the dedicated Merchant API checkout registration and soft-deletes the link while preserving payment and settlement history.",
        "operationId": "deletePaymentLink",
        "parameters": [{ "$ref": "#/components/parameters/ResourceId" }],
        "responses": {
          "200": {
            "description": "Deleted payment link",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["id", "object", "deleted", "request_id"],
                  "properties": {
                    "id": { "type": "string", "format": "uuid" },
                    "object": { "const": "payment_link" },
                    "deleted": { "const": true },
                    "request_id": { "$ref": "#/components/schemas/RequestId" }
                  }
                }
              }
            }
          },
          "default": { "$ref": "#/components/responses/ApiError" }
        }
      }
    },
    "/checkouts": {
      "get": {
        "tags": ["Checkouts"],
        "summary": "List checkout entities",
        "description": "Requires links:read or links:*.",
        "operationId": "listCheckouts",
        "parameters": [
          { "$ref": "#/components/parameters/Limit" },
          { "$ref": "#/components/parameters/StartingAfter" },
          { "name": "type", "in": "query", "schema": { "type": "string", "enum": ["hosted_page", "payment_link", "pricing_table", "widget"] }, "description": "Exact checkout type filter. Any other value returns 400 invalid_filter with param type." },
          { "name": "status", "in": "query", "schema": { "type": "string", "enum": ["draft", "live", "deactivated", "archived"] }, "description": "Exact checkout status filter. Any other value returns 400 invalid_filter with param status." }
        ],
        "responses": {
          "200": {
            "description": "Cursor page",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ListEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": { "$ref": "#/components/schemas/Checkout" }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "default": { "$ref": "#/components/responses/ApiError" }
        }
      },
      "post": {
        "tags": ["Checkouts"],
        "summary": "Create a checkout draft",
        "description": "Requires links:write or links:*. Creates a typed draft and optional immutable routing version.",
        "operationId": "createCheckout",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/CheckoutCreate" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Draft checkout",
            "content": {
              "application/json": {
                "schema": { "allOf": [{ "$ref": "#/components/schemas/Checkout" }, { "$ref": "#/components/schemas/RequestIdEnvelope" }] }
              }
            }
          },
          "default": { "$ref": "#/components/responses/ApiError" }
        }
      }
    },
    "/checkouts/{id}": {
      "get": {
        "tags": ["Checkouts"],
        "summary": "Retrieve a checkout",
        "description": "Requires links:read or links:*. Returns separate draft routing and published live routing snapshots.",
        "operationId": "retrieveCheckout",
        "parameters": [{ "$ref": "#/components/parameters/ResourceId" }],
        "responses": {
          "200": {
            "description": "Checkout entity",
            "content": {
              "application/json": {
                "schema": { "allOf": [{ "$ref": "#/components/schemas/Checkout" }, { "$ref": "#/components/schemas/RequestIdEnvelope" }] }
              }
            }
          },
          "default": { "$ref": "#/components/responses/ApiError" }
        }
      },
      "patch": {
        "tags": ["Checkouts"],
        "summary": "Update a checkout draft",
        "description": "Requires links:write or links:*. Updates name/config draft state and/or advances the immutable routing version without publishing the config draft.",
        "operationId": "updateCheckout",
        "parameters": [{ "$ref": "#/components/parameters/ResourceId" }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/CheckoutUpdate" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated checkout draft",
            "content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/Checkout" }, { "$ref": "#/components/schemas/RequestIdEnvelope" }] } } }
          },
          "409": {
            "description": "checkout_archived when the checkout is archived; concurrent_update when it changed concurrently (safe to retry).",
            "$ref": "#/components/responses/ApiError"
          },
          "default": { "$ref": "#/components/responses/ApiError" }
        }
      }
    },
    "/checkouts/{id}/publish": {
      "post": {
        "tags": ["Checkouts"],
        "summary": "Publish a checkout",
        "description": "Requires links:write or links:*. Server-fingerprints the current config, routing, and registration intent, then atomically promotes that exact draft through the checkout lifecycle RPC. Publishing through the API promotes configuration and routing only; product and tier registrations that make the page payable are created in the Dashboard. For a fully API-driven payable flow use POST /payment_links.",
        "operationId": "publishCheckout",
        "parameters": [{ "$ref": "#/components/parameters/ResourceId" }],
        "responses": {
          "200": {
            "description": "Published live checkout",
            "content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/Checkout" }, { "$ref": "#/components/schemas/RequestIdEnvelope" }] } } }
          },
          "409": {
            "description": "checkout_archived when the checkout is archived; concurrent_update when it changed concurrently (safe to retry).",
            "$ref": "#/components/responses/ApiError"
          },
          "default": { "$ref": "#/components/responses/ApiError" }
        }
      }
    },
    "/checkouts/{id}/deactivate": {
      "post": {
        "tags": ["Checkouts"],
        "summary": "Deactivate a checkout",
        "description": "Requires links:write or links:*. The checkout-entity lifecycle does not cover unregistered legacy, Shopify redirect, or standalone links.",
        "operationId": "deactivateCheckout",
        "parameters": [{ "$ref": "#/components/parameters/ResourceId" }],
        "responses": {
          "200": {
            "description": "Deactivated checkout",
            "content": {
              "application/json": {
                "schema": { "allOf": [{ "$ref": "#/components/schemas/Checkout" }, { "$ref": "#/components/schemas/RequestIdEnvelope" }] }
              }
            }
          },
          "default": { "$ref": "#/components/responses/ApiError" }
        }
      }
    },
    "/routing": {
      "get": {
        "tags": ["Routing"],
        "summary": "Retrieve account routing policy",
        "description": "Requires links:read or links:*.",
        "operationId": "retrieveRoutingPolicy",
        "responses": {
          "200": {
            "description": "Current account routing policy",
            "content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/RoutingPolicy" }, { "$ref": "#/components/schemas/RequestIdEnvelope" }] } } }
          },
          "default": { "$ref": "#/components/responses/ApiError" }
        }
      },
      "patch": {
        "tags": ["Routing"],
        "summary": "Replace account routing policy",
        "description": "Requires links:write or links:*. Enabled providers are the hard ceiling for every checkout.",
        "operationId": "updateRoutingPolicy",
        "requestBody": {
          "required": true,
          "content": { "application/json": { "schema": { "$ref": "#/components/schemas/RoutingPolicyUpdate" } } }
        },
        "responses": {
          "200": {
            "description": "Replacement account routing policy",
            "content": { "application/json": { "schema": { "allOf": [{ "$ref": "#/components/schemas/RoutingPolicy" }, { "$ref": "#/components/schemas/RequestIdEnvelope" }] } } }
          },
          "400": {
            "description": "invalid_provider_ids (param enabled_provider_ids) when the set contains an unverified provider or is otherwise rejected by the database.",
            "$ref": "#/components/responses/ApiError"
          },
          "default": { "$ref": "#/components/responses/ApiError" }
        }
      }
    },
    "/payments": {
      "get": {
        "tags": ["Payments"],
        "summary": "List payments",
        "description": "Requires payments:read. This route has cursor pagination but no status or date filters.",
        "operationId": "listPayments",
        "parameters": [
          { "$ref": "#/components/parameters/Limit" },
          { "$ref": "#/components/parameters/StartingAfter" }
        ],
        "responses": {
          "200": {
            "description": "Cursor page",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ListEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": { "$ref": "#/components/schemas/Payment" }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "default": { "$ref": "#/components/responses/ApiError" }
        }
      }
    },
    "/payments/{id}": {
      "get": {
        "tags": ["Payments"],
        "summary": "Retrieve a payment",
        "description": "Requires payments:read.",
        "operationId": "retrievePayment",
        "parameters": [{ "$ref": "#/components/parameters/ResourceId" }],
        "responses": {
          "200": {
            "description": "Payment ledger row",
            "content": {
              "application/json": {
                "schema": { "allOf": [{ "$ref": "#/components/schemas/Payment" }, { "$ref": "#/components/schemas/RequestIdEnvelope" }] }
              }
            }
          },
          "default": { "$ref": "#/components/responses/ApiError" }
        }
      }
    },
    "/wallets": {
      "get": {
        "tags": ["Wallets"],
        "summary": "List payout wallets",
        "description": "Requires payments:read. Wallet mutation is dashboard-managed.",
        "operationId": "listWallets",
        "parameters": [
          { "$ref": "#/components/parameters/Limit" },
          { "$ref": "#/components/parameters/StartingAfter" }
        ],
        "responses": {
          "200": {
            "description": "Cursor page",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ListEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": { "$ref": "#/components/schemas/Wallet" }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "default": { "$ref": "#/components/responses/ApiError" }
        }
      }
    },
    "/providers": {
      "get": {
        "tags": ["Providers"],
        "summary": "List curated routing providers",
        "description": "Requires payments:read. Returns verified brand identity and the immutable current capability snapshot; the public operational utility remains a separate surface.",
        "operationId": "listProviders",
        "parameters": [
          { "$ref": "#/components/parameters/Limit" },
          { "$ref": "#/components/parameters/StartingAfter" }
        ],
        "responses": {
          "200": {
            "description": "Cursor page",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ListEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": { "$ref": "#/components/schemas/Provider" }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "default": { "$ref": "#/components/responses/ApiError" }
        }
      }
    },
    "/merchant/balance": {
      "get": {
        "tags": ["Balance"],
        "summary": "Retrieve the merchant balance rollup",
        "description": "Requires payments:read. This is a computed rollup, not a bank or payout-processor ledger: availability is based on a 24-hour age rule, in_transit_balance is currently zero, and next payout is an estimate.",
        "operationId": "retrieveMerchantBalance",
        "responses": {
          "200": {
            "description": "Computed balance rollup",
            "content": {
              "application/json": {
                "schema": { "allOf": [{ "$ref": "#/components/schemas/MerchantBalance" }, { "$ref": "#/components/schemas/RequestIdEnvelope" }] }
              }
            }
          },
          "default": { "$ref": "#/components/responses/ApiError" }
        }
      }
    },
    "/webhook_endpoints": {
      "get": {
        "tags": ["Webhook endpoints"],
        "summary": "List webhook endpoints",
        "description": "Requires webhooks:read. Secrets are never returned by list.",
        "operationId": "listWebhookEndpoints",
        "parameters": [
          { "$ref": "#/components/parameters/Limit" },
          { "$ref": "#/components/parameters/StartingAfter" }
        ],
        "responses": {
          "200": {
            "description": "Cursor page",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/ListEnvelope" },
                    {
                      "type": "object",
                      "properties": {
                        "data": {
                          "type": "array",
                          "items": { "$ref": "#/components/schemas/WebhookEndpoint" }
                        }
                      }
                    }
                  ]
                }
              }
            }
          },
          "default": { "$ref": "#/components/responses/ApiError" }
        }
      },
      "post": {
        "tags": ["Webhook endpoints"],
        "summary": "Create a webhook endpoint",
        "description": "Requires webhooks:write. The public HTTPS URL is SSRF-checked. The whsec_ signing secret is returned once.",
        "operationId": "createWebhookEndpoint",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/WebhookEndpointCreate" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Created endpoint with one-time secret",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/WebhookEndpoint" },
                    { "$ref": "#/components/schemas/RequestIdEnvelope" },
                    { "type": "object", "required": ["secret"], "properties": { "secret": { "type": "string", "pattern": "^whsec_" } } }
                  ]
                }
              }
            }
          },
          "default": { "$ref": "#/components/responses/ApiError" }
        }
      }
    },
    "/webhook_endpoints/{id}": {
      "patch": {
        "tags": ["Webhook endpoints"],
        "summary": "Update a webhook endpoint",
        "description": "Requires webhooks:write. Supply at least one of url, events, description, or active.",
        "operationId": "updateWebhookEndpoint",
        "parameters": [{ "$ref": "#/components/parameters/ResourceId" }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/WebhookEndpointUpdate" }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated endpoint",
            "content": {
              "application/json": {
                "schema": { "allOf": [{ "$ref": "#/components/schemas/WebhookEndpoint" }, { "$ref": "#/components/schemas/RequestIdEnvelope" }] }
              }
            }
          },
          "default": { "$ref": "#/components/responses/ApiError" }
        }
      },
      "delete": {
        "tags": ["Webhook endpoints"],
        "summary": "Soft-delete a webhook endpoint",
        "description": "Requires webhooks:write. The route returns HTTP 200 and preserves historical audit rows.",
        "operationId": "deleteWebhookEndpoint",
        "parameters": [{ "$ref": "#/components/parameters/ResourceId" }],
        "responses": {
          "200": {
            "description": "Deleted endpoint marker",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": ["id", "object", "deleted", "request_id"],
                  "properties": {
                    "id": { "type": "string", "format": "uuid" },
                    "object": { "const": "webhook_endpoint" },
                    "deleted": { "const": true },
                    "request_id": { "type": "string" }
                  }
                }
              }
            }
          },
          "default": { "$ref": "#/components/responses/ApiError" }
        }
      }
    },
    "/webhook_endpoints/{id}/rotate_secret": {
      "post": {
        "tags": ["Webhook endpoints"],
        "summary": "Rotate a webhook signing secret",
        "description": "Requires webhooks:write. The new secret is returned once. The previous secret remains available to the delivery worker for a five-minute grace period.",
        "operationId": "rotateWebhookSecret",
        "parameters": [{ "$ref": "#/components/parameters/ResourceId" }],
        "responses": {
          "200": {
            "description": "Endpoint with one-time replacement secret",
            "content": {
              "application/json": {
                "schema": {
                  "allOf": [
                    { "$ref": "#/components/schemas/WebhookEndpoint" },
                    { "$ref": "#/components/schemas/RequestIdEnvelope" },
                    { "type": "object", "required": ["secret"], "properties": { "secret": { "type": "string", "pattern": "^whsec_" } } }
                  ]
                }
              }
            }
          },
          "default": { "$ref": "#/components/responses/ApiError" }
        }
      }
    },
    "/webhook_endpoints/{id}/test": {
      "post": {
        "tags": ["Webhook endpoints"],
        "summary": "Queue a test event",
        "description": "Requires webhooks:write. The endpoint must be active and the event must already be subscribed. Delivery is asynchronous through the production delivery worker.",
        "operationId": "testWebhookEndpoint",
        "parameters": [{ "$ref": "#/components/parameters/ResourceId" }],
        "requestBody": {
          "required": false,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "event_type": { "$ref": "#/components/schemas/WebhookEventType" },
                  "event": { "$ref": "#/components/schemas/WebhookEventType" }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Queued test marker",
            "content": {
              "application/json": {
                "schema": { "allOf": [{ "$ref": "#/components/schemas/WebhookTest" }, { "$ref": "#/components/schemas/RequestIdEnvelope" }] }
              }
            }
          },
          "default": { "$ref": "#/components/responses/ApiError" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "mgp_live_ or mgp_test_ API key",
        "description": "Create scoped keys in Dashboard → Settings. Keep them server-side."
      }
    },
    "parameters": {
      "ResourceId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": { "type": "string", "format": "uuid" }
      },
      "Limit": {
        "name": "limit",
        "in": "query",
        "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 },
        "description": "Page size. Values above 100 are clamped; invalid or non-positive values return 400."
      },
      "StartingAfter": {
        "name": "starting_after",
        "in": "query",
        "schema": { "type": "string" },
        "description": "Opaque next_cursor from the previous response."
      },
      "IdempotencyKey": {
        "name": "Idempotency-Key",
        "in": "header",
        "schema": { "type": "string", "maxLength": 160 },
        "description": "Organization-scoped key for payment-link registration. Reuse it only for the same logical create and reuse tracking_id too. Reusing either identity with changed input returns 409 idempotency_payload_mismatch."
      }
    },
    "responses": {
      "ApiError": {
        "description": "API error",
        "content": {
          "application/json": {
            "schema": { "$ref": "#/components/schemas/ErrorEnvelope" }
          }
        }
      }
    },
    "schemas": {
      "RequestId": {
        "type": "string",
        "description": "Also returned in the X-Request-Id response header."
      },
      "WebhookPaymentEvent": {
        "type": "object",
        "description": "Body of production payment.* webhook deliveries. Sent as the entire JSON body with no outer envelope; the event type is in the X-MGPay-Event-Type header. Reconcile by tracking_id.",
        "required": ["payment_id", "payment_link_id", "tracking_id", "organization_id", "status", "currency", "amount_expected", "pending"],
        "properties": {
          "payment_id": { "type": "string", "format": "uuid" },
          "payment_link_id": { "type": ["string", "null"], "format": "uuid" },
          "tracking_id": { "type": ["string", "null"], "description": "The tracking_id supplied to or returned by POST /payment_links." },
          "organization_id": { "type": "string", "format": "uuid" },
          "status": { "enum": ["detected", "confirming", "completed", "expired", "failed"] },
          "currency": { "type": ["string", "null"] },
          "amount_expected": { "type": ["number", "null"] },
          "amount_received": { "type": ["number", "null"], "description": "USD value for completed events; coin amount otherwise." },
          "value_coin": { "type": ["number", "null"] },
          "value_usd": { "type": ["number", "null"] },
          "expected_usd": { "type": ["number", "null"] },
          "settlement_currency": { "type": ["string", "null"] },
          "coin": { "type": ["string", "null"] },
          "network": { "type": ["string", "null"] },
          "txid_in": { "type": ["string", "null"] },
          "txid_out": { "type": ["string", "null"], "description": "Present only when status is completed." },
          "pending": { "type": "boolean" },
          "callback_id": { "type": "integer", "description": "Internal callback row id; not a stable key." }
        }
      },
      "RequestIdEnvelope": {
        "type": "object",
        "description": "Every top-level JSON body carries request_id (also sent as the X-Request-Id header). Objects nested inside a list data array do not repeat it.",
        "required": ["request_id"],
        "properties": { "request_id": { "$ref": "#/components/schemas/RequestId" } }
      },
      "ListEnvelope": {
        "type": "object",
        "required": ["object", "url", "has_more", "next_cursor", "data", "request_id"],
        "properties": {
          "object": { "const": "list" },
          "url": { "type": "string" },
          "has_more": { "type": "boolean" },
          "next_cursor": { "type": ["string", "null"] },
          "data": { "type": "array", "items": {} },
          "request_id": { "$ref": "#/components/schemas/RequestId" }
        }
      },
      "ErrorEnvelope": {
        "type": "object",
        "required": ["error"],
        "properties": {
          "error": {
            "type": "object",
            "required": ["type", "code", "message", "request_id"],
            "properties": {
              "type": { "type": "string", "examples": ["invalid_request_error"] },
              "code": { "type": "string", "examples": ["missing_required_param"] },
              "message": { "type": "string" },
              "param": { "type": ["string", "null"] },
              "request_id": { "$ref": "#/components/schemas/RequestId" }
            }
          }
        }
      },
      "Merchant": {
        "type": "object",
        "required": ["object", "id", "name", "status", "plan", "api_key"],
        "properties": {
          "object": { "const": "merchant" },
          "id": { "type": "string", "format": "uuid" },
          "slug": { "type": ["string", "null"] },
          "name": { "type": "string" },
          "status": { "type": "string" },
          "plan": { "type": "string" },
          "website": { "type": ["string", "null"] },
          "support_email": { "type": ["string", "null"] },
          "timezone": { "type": ["string", "null"] },
          "api_key": {
            "type": "object",
            "required": ["id", "environment", "scopes"],
            "properties": {
              "id": { "type": "string", "format": "uuid" },
              "environment": { "enum": ["live", "test"] },
              "scopes": { "type": "array", "items": { "type": "string" } }
            }
          },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "PaymentLinkCreate": {
        "type": "object",
        "required": ["amount"],
        "properties": {
          "amount": { "type": "number", "minimum": 1, "maximum": 100000 },
          "currency": { "enum": ["USD", "EUR", "CAD", "GBP"], "default": "USD" },
          "customer_email": { "type": "string", "maxLength": 320, "description": "Currently truncated but not format-validated." },
          "business_name": { "type": "string", "maxLength": 160 },
          "description": { "type": "string", "maxLength": 500 },
          "product_name": { "type": "string", "maxLength": 160 },
          "success_url": { "type": "string", "format": "uri", "maxLength": 2048, "description": "Absolute http(s) URL. After the payment is confirmed the hosted checkout forwards the buyer here with mgpay_tracking_id and mgpay_status=completed appended; the tracking page offers a Continue to merchant action. When set, the payment provider opens in a new tab so the checkout can return the buyer." },
          "cancel_url": { "type": "string", "format": "uri", "maxLength": 2048 },
          "network": { "type": "string", "default": "polygon" },
          "token": { "type": "string", "default": "usdc" },
          "checkout_style": { "type": "string", "enum": ["page", "widget"], "default": "page" },
          "tracking_id": { "type": "string", "pattern": "^[A-Za-z0-9_-]{6,80}$" },
          "idempotency_key": { "type": "string", "maxLength": 160, "description": "Body fallback when the Idempotency-Key header is absent." }
        },
        "additionalProperties": true
      },
      "PaymentLink": {
        "type": "object",
        "required": ["id", "object", "amount", "currency", "status", "payment_url", "tracking_id", "tracking_url", "metadata", "created_at"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "object": { "const": "payment_link" },
          "amount": { "type": "number" },
          "currency": { "type": "string" },
          "customer_email": { "type": ["string", "null"] },
          "description": { "type": ["string", "null"] },
          "status": { "type": "string" },
          "payment_url": { "type": "string", "format": "uri", "description": "Tokenless direct buyer handoff. Navigate to this exact value without constructing, canonicalizing, or adding provider parameters. Merchant API links carry an immutable provider snapshot of up to eight providers drawn from the account Smart Routing policy." },
          "tracking_id": { "type": "string" },
          "tracking_url": { "type": "string", "format": "uri", "description": "Tokenless buyer-facing tracking URL." },
          "expires_at": { "type": ["string", "null"], "format": "date-time", "description": "Always null for merchant-owned persistent links. Retained for backward-compatible transaction-attempt responses." },
          "paid_at": { "type": ["string", "null"], "format": "date-time" },
          "paid_amount": { "type": ["number", "null"] },
          "tx_id": { "type": ["string", "null"] },
          "metadata": { "type": "object", "additionalProperties": true },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "CheckoutRoutingInput": {
        "type": "object",
        "required": ["version", "mode", "providerIds"],
        "properties": {
          "version": { "const": 1 },
          "mode": { "enum": ["inherit", "smart", "customer_choice", "single"] },
          "providerIds": {
            "type": "array",
            "maxItems": 8,
            "uniqueItems": true,
            "items": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_-]{0,63}$" }
          }
        },
        "additionalProperties": false,
        "description": "Request form of a routing config. Send exactly version, mode, and providerIds; any other key (including versionId or versionNumber read from a response) returns 400 invalid_routing_config. Cardinality is exact: inherit 0; smart 0-8; customer_choice 1-8; single exactly 1."
      },
      "CheckoutRouting": {
        "type": "object",
        "required": ["version", "mode", "providerIds"],
        "properties": {
          "version": { "const": 1 },
          "mode": { "enum": ["inherit", "smart", "customer_choice", "single"] },
          "providerIds": {
            "type": "array",
            "maxItems": 8,
            "uniqueItems": true,
            "items": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_-]{0,63}$" }
          },
          "versionId": { "type": "string", "format": "uuid", "readOnly": true },
          "versionNumber": { "type": "integer", "minimum": 1, "readOnly": true }
        },
        "additionalProperties": false,
        "description": "Cardinality is exact: inherit 0; smart 0-8; customer_choice 1-8; single exactly 1."
      },
      "CheckoutCreate": {
        "type": "object",
        "required": ["name", "type"],
        "properties": {
          "name": { "type": "string", "maxLength": 120 },
          "type": { "enum": ["hosted_page", "payment_link", "pricing_table", "widget"] },
          "config": { "type": "object", "additionalProperties": true, "description": "Typed JSON draft configuration returned unchanged." },
          "routing": { "$ref": "#/components/schemas/CheckoutRoutingInput" }
        },
        "additionalProperties": true
      },
      "CheckoutUpdate": {
        "type": "object",
        "minProperties": 1,
        "properties": {
          "name": { "type": "string", "maxLength": 120 },
          "config": { "type": "object", "additionalProperties": true },
          "routing": { "$ref": "#/components/schemas/CheckoutRoutingInput" }
        },
        "additionalProperties": false
      },
      "Checkout": {
        "type": "object",
        "required": ["id", "object", "name", "type", "status", "public_id", "url", "draft_config", "live_config", "routing", "live_routing", "created_at", "updated_at", "published_at"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "object": { "const": "checkout" },
          "name": { "type": "string" },
          "type": { "type": "string" },
          "status": { "enum": ["draft", "live", "deactivated", "archived"] },
          "public_id": { "type": ["string", "null"] },
          "url": { "type": ["string", "null"], "format": "uri", "description": "Only populated for a live checkout. Use exactly as returned." },
          "draft_config": { "type": "object", "additionalProperties": true },
          "live_config": { "type": ["object", "null"], "additionalProperties": true },
          "routing": { "oneOf": [{ "$ref": "#/components/schemas/CheckoutRouting" }, { "type": "null" }], "description": "Current private draft routing version. It does not affect buyers until publish." },
          "live_routing": { "oneOf": [{ "$ref": "#/components/schemas/CheckoutRouting" }, { "type": "null" }], "description": "Exact published routing version used by buyer sessions and Shopify release snapshots." },
          "created_at": { "type": "string", "format": "date-time" },
          "updated_at": { "type": "string", "format": "date-time" },
          "published_at": { "type": ["string", "null"], "format": "date-time" }
        }
      },
      "Payment": {
        "type": "object",
        "required": ["id", "object", "status", "amount_expected", "currency", "metadata", "created_at", "updated_at"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "object": { "const": "payment" },
          "status": { "enum": ["created", "awaiting_address", "awaiting_payment", "detected", "confirming", "completed", "expired", "failed", "refunded", "cancelled"] },
          "amount_expected": { "type": "number" },
          "amount_received": { "type": ["number", "null"] },
          "currency": { "type": "string" },
          "coin": { "type": ["string", "null"] },
          "network": { "type": ["string", "null"] },
          "provider": { "type": ["string", "null"] },
          "payment_link_id": { "type": ["string", "null"], "format": "uuid" },
          "customer_email": { "type": ["string", "null"] },
          "txid_in": { "type": ["string", "null"] },
          "txid_out": { "type": ["string", "null"] },
          "confirmations": { "type": ["integer", "null"] },
          "required_confirmations": { "type": ["integer", "null"] },
          "metadata": { "type": "object", "additionalProperties": true },
          "detected_at": { "type": ["string", "null"], "format": "date-time" },
          "confirmed_at": { "type": ["string", "null"], "format": "date-time" },
          "settled_at": { "type": ["string", "null"], "format": "date-time" },
          "expired_at": { "type": ["string", "null"], "format": "date-time" },
          "failed_at": { "type": ["string", "null"], "format": "date-time" },
          "refunded_at": { "type": ["string", "null"], "format": "date-time" },
          "created_at": { "type": "string", "format": "date-time" },
          "updated_at": { "type": "string", "format": "date-time" }
        }
      },
      "Wallet": {
        "type": "object",
        "required": ["id", "object", "address", "network", "currency", "is_default", "created_at"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "object": { "const": "wallet" },
          "name": { "type": ["string", "null"] },
          "address": { "type": "string" },
          "network": { "type": "string" },
          "currency": { "type": "string" },
          "is_default": { "type": "boolean" },
          "label": { "type": ["string", "null"] },
          "verified_at": { "type": ["string", "null"], "format": "date-time" },
          "last_used_at": { "type": ["string", "null"], "format": "date-time" },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "RoutingPolicyUpdate": {
        "type": "object",
        "required": ["enabled_provider_ids"],
        "properties": {
          "enabled_provider_ids": {
            "type": "array",
            "maxItems": 100,
            "uniqueItems": true,
            "items": { "type": "string", "pattern": "^[a-z0-9][a-z0-9_-]{0,63}$" }
          }
        },
        "additionalProperties": false
      },
      "RoutingPolicy": {
        "type": "object",
        "required": ["object", "state", "enabled_provider_ids"],
        "properties": {
          "object": { "const": "routing_policy" },
          "state": { "enum": ["unconfigured", "configured"] },
          "version_id": { "type": ["string", "null"], "format": "uuid" },
          "version_number": { "type": ["integer", "null"] },
          "enabled_provider_ids": { "type": "array", "items": { "type": "string" } },
          "config_hash": { "type": ["string", "null"] },
          "replayed": { "type": "boolean" }
        }
      },
      "ProviderCapability": {
        "type": "object",
        "required": ["version_id", "version_number", "country_codes", "currency_codes", "payment_methods", "wallet_capabilities", "country_fit", "reliability_score", "operationally_bookable", "observed_at"],
        "properties": {
          "version_id": { "type": "string", "format": "uuid" },
          "version_number": { "type": "integer" },
          "country_codes": { "type": "array", "items": { "type": "string", "pattern": "^[A-Z]{2}$" } },
          "currency_codes": { "type": "array", "items": { "type": "string", "pattern": "^[A-Z]{3}$" } },
          "min_amount": { "type": ["number", "null"] },
          "max_amount": { "type": ["number", "null"] },
          "payment_methods": { "type": "array" },
          "wallet_capabilities": { "type": "array" },
          "kyc_friction": { "type": ["integer", "null"] },
          "country_fit": { "type": "object", "additionalProperties": { "type": "number" } },
          "reliability_score": { "type": "number" },
          "operationally_bookable": { "type": "boolean" },
          "observed_at": { "type": "string", "format": "date-time" }
        }
      },
      "Provider": {
        "type": "object",
        "required": ["id", "object", "name", "benefits", "limitations", "verified", "platform_enabled", "operational_status", "capability"],
        "properties": {
          "id": { "type": "string", "description": "Provider slug." },
          "object": { "const": "routing_provider" },
          "name": { "type": "string" },
          "logo_url": { "type": ["string", "null"] },
          "description": { "type": ["string", "null"] },
          "benefits": { "type": "array", "items": { "type": "string" } },
          "limitations": { "type": "array", "items": { "type": "string" } },
          "verified": { "const": true },
          "platform_enabled": { "type": "boolean" },
          "operational_status": { "enum": ["active", "suspended"] },
          "capability": { "oneOf": [{ "$ref": "#/components/schemas/ProviderCapability" }, { "type": "null" }] }
        }
      },
      "BalanceBlock": {
        "type": "object",
        "required": ["gross_volume", "net_volume", "total_fees", "available_balance", "pending_balance", "total_transactions"],
        "properties": {
          "gross_volume": { "type": "number" },
          "net_volume": { "type": "number" },
          "total_fees": { "type": "number" },
          "available_balance": { "type": "number" },
          "pending_balance": { "type": "number" },
          "total_transactions": { "type": "integer" }
        }
      },
      "MerchantBalance": {
        "type": "object",
        "required": ["object", "available_balance", "pending_balance", "in_transit_balance", "total_received", "gross_volume", "net_volume", "total_fees", "total_transactions", "next_payout_date", "next_payout_amount", "primary_currency", "totals_per_currency"],
        "properties": {
          "object": { "const": "merchant_balance" },
          "available_balance": { "type": "number" },
          "pending_balance": { "type": "number" },
          "in_transit_balance": { "type": "number", "description": "Currently always zero." },
          "total_received": { "type": "number" },
          "gross_volume": { "type": "number" },
          "net_volume": { "type": "number" },
          "total_fees": { "type": "number" },
          "total_transactions": { "type": "integer" },
          "next_payout_date": { "type": "string", "format": "date", "description": "Computed as the following day, not sourced from a payout processor." },
          "next_payout_amount": { "type": "number" },
          "primary_currency": { "type": "string" },
          "totals_per_currency": { "type": "object", "additionalProperties": { "$ref": "#/components/schemas/BalanceBlock" } }
        }
      },
      "WebhookEventType": {
        "type": "string",
        "enum": ["payment.created", "payment.detected", "payment.completed", "payment.failed", "payment.expired", "payment.refunded", "payout.processed", "kyc.status_changed", "merchant.activated"],
        "description": "Production producers currently exist for payment.detected, payment.completed, payment.failed, payment.expired, and payment.refunded. Other values are accepted for subscriptions/tests but are not general production emissions."
      },
      "WebhookEndpointCreate": {
        "type": "object",
        "required": ["url"],
        "properties": {
          "url": { "type": "string", "format": "uri", "description": "Public HTTPS terminal URL; localhost/private/reserved targets and redirects are unsupported." },
          "events": { "type": "array", "minItems": 1, "uniqueItems": true, "items": { "$ref": "#/components/schemas/WebhookEventType" }, "default": ["payment.completed"] },
          "description": { "type": "string", "maxLength": 300 },
          "active": { "type": "boolean", "default": true }
        }
      },
      "WebhookEndpointUpdate": {
        "type": "object",
        "minProperties": 1,
        "properties": {
          "url": { "type": "string", "format": "uri" },
          "events": { "type": "array", "minItems": 1, "uniqueItems": true, "items": { "$ref": "#/components/schemas/WebhookEventType" } },
          "description": { "type": "string", "maxLength": 300 },
          "active": { "type": "boolean" }
        }
      },
      "WebhookEndpoint": {
        "type": "object",
        "required": ["id", "object", "url", "secret_hint", "events", "active", "consecutive_failures", "created_at", "updated_at"],
        "properties": {
          "id": { "type": "string", "format": "uuid" },
          "object": { "const": "webhook_endpoint" },
          "url": { "type": "string", "format": "uri" },
          "description": { "type": ["string", "null"] },
          "secret_hint": { "type": "string", "description": "Last four characters only." },
          "events": { "type": "array", "items": { "$ref": "#/components/schemas/WebhookEventType" } },
          "active": { "type": "boolean" },
          "last_triggered_at": { "type": ["string", "null"], "format": "date-time" },
          "last_success_at": { "type": ["string", "null"], "format": "date-time" },
          "last_failure_at": { "type": ["string", "null"], "format": "date-time" },
          "consecutive_failures": { "type": "integer", "minimum": 0 },
          "created_at": { "type": "string", "format": "date-time" },
          "updated_at": { "type": "string", "format": "date-time" }
        }
      },
      "WebhookTest": {
        "type": "object",
        "required": ["object", "ok", "event_id", "endpoint_id", "event_type", "created_at"],
        "properties": {
          "object": { "const": "webhook_test" },
          "ok": { "const": true },
          "event_id": { "type": "string", "format": "uuid" },
          "endpoint_id": { "type": "string", "format": "uuid" },
          "event_type": { "$ref": "#/components/schemas/WebhookEventType" },
          "created_at": { "type": "string", "format": "date-time" }
        }
      }
    }
  },
  "x-mgpay-webhook-delivery": {
    "signature": "HMAC-SHA256 over ${timestamp}.${rawBody}",
    "headers": ["X-MGPay-Delivery-Id", "X-MGPay-Event-Id", "X-MGPay-Event-Type", "X-MGPay-Timestamp", "X-MGPay-Signature", "X-MGPay-Signature-Previous"],
    "timestampToleranceSeconds": 300,
    "requestTimeoutSeconds": 10,
    "maxAttemptsForNewDeliveries": 36,
    "maximumBackoffSeconds": 21600,
    "success": "Any 2xx",
    "permanentFailures": ["HTTP 410", "Any 3xx redirect"],
    "retryableFailures": ["Other non-2xx responses", "Network errors", "Timeouts"]
  },
  "x-mgpay-rate-limits": {
    "ipRoutePerMinute": 120,
    "apiKeyRoutePerMinute": 600,
    "productionStorage": "Per-isolate memory; no shared KV binding is currently provisioned",
    "headersOn429": ["Retry-After", "X-RateLimit-Limit", "X-RateLimit-Reset"]
  }
}
