{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-platform/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["steps"]},"type":"markdown"},"seo":{"title":"Webhooks","llmstxt":{"title":"Glomopay developer documentation","description":"Cross-border payments APIs, organised by product. Each product section carries both the explanation of its objects and the job-shaped integration guides that set them up.","details":{"content":"How to use these docs:\n\n- Get started is the front door. It carries a routing table from\n  \"what are you building\" to the page that builds it, plus how to get\n  API keys.\n- The product sections - Payin, Payout, Multi-currency accounts,\n  Verification, Request for Information (RFI) - each hold two kinds of page.\n  Pages titled as a noun (\"Subscriptions\", \"Bank Transfer\") explain what\n  an object is. Pages titled as a job (\"Set up recurring subscriptions\",\n  \"Collect LRS remittances from India\") are complete integration flows\n  and live one level under the object they set up. Match a user's goal\n  against the job-shaped titles first.\n- Reports and Identity and Access Management are the two subjects with no\n  API counterpart. There are no endpoints for reports, users, roles or\n  invites; both pages document the merchant dashboard. A dashboard\n  procedure for something the API also does (a refund, a payout) is NOT\n  here - it stays in the product that owns it.\n- API reference is generated from OpenAPI and carries endpoint detail.\n- The OpenAPI description itself is published as a single self-contained\n  document at /openapi.yaml, and the same document as JSON at\n  /openapi.json. Every $ref is resolved into it, so one fetch is the\n  whole contract: 61 paths, 81 operations, 68 schemas - the same\n  endpoints the API reference pages render. Fetch that instead of\n  reading the 129 pages under /api-reference/.\n- Developer resources covers what every integration touches and no single\n  product owns: auth, errors, pagination, rate limits, webhooks,\n  versioning, supported countries, quotes, and the client SDKs.\n"},"sections":[{"title":"Get started","description":"The front door. The routing table from a merchant's goal to the flow that delivers it, how to obtain API keys, and the platform glossary.","includeFiles":["**/get-started/**"]},{"title":"Payin","description":"Collecting money: customers, orders, checkout, payment links, payment methods (cards, bank transfer, pay via bank), refunds, disputes, purpose codes, fee models, custom fields, subscriptions, and resident India remittance under LRS. Includes the flows that set each up - bank transfers, subscriptions, LRS remittances, and server-to-server card payments.","includeFiles":["**/payin/**"]},{"title":"Payout","description":"Sending money: the payout life cycle, queued payouts, creating and cancelling a payout, rails, beneficiaries, purpose codes, and the action required state.","includeFiles":["**/payout/**"]},{"title":"Multi-currency accounts","description":"Holding money: balances, balance conversion, adding balance, withdrawing balance, and the settlement holiday calendar - the Indian and US bank and FX holidays on which settlement to the merchant's bank account, and money movement generally, is affected.","includeFiles":["**/multi-currency-account/**"]},{"title":"Verification","description":"Proving who someone is and that an account is theirs: KYC, bank account validation, and third-party verification (TPV) with the flow that verifies a payer's bank account.","includeFiles":["**/verification/**"]},{"title":"Request for Information (RFI)","description":"Responding to a compliance request for information, per object - payment link, order, payment, payout - plus compliance reviews and RFIs raised on already-successful payments.","includeFiles":["**/request-for-information/**"]},{"title":"Reports","description":"The downloadable and scheduled reports a merchant pulls from the dashboard for reconciliation, compliance and treasury: payment, subscription, settlement breakup, balance statement and KYC reports, plus scheduling one for recurring email delivery. Dashboard only - there is no reports API.","includeFiles":["**/reports/**"]},{"title":"Identity and Access Management","description":"Administering who can use a merchant account: one set of credentials across multiple MIDs, switching between them without logging out, the Admin / Member / Sales Partner roles and what each may see and do, and inviting a new member. Dashboard only - there is no users, roles or invites API.","includeFiles":["**/access-management/**"]},{"title":"API reference","description":"Endpoint reference generated from the Glomopay OpenAPI description.","includeFiles":["**/api-reference/**"]},{"title":"Developer resources","description":"What every integration touches and no single product owns: auth, errors, pagination, rate limits, webhooks, versioning, API validations, security, supported countries, quotes, and the client SDKs - React Native, Flutter, Android and the Unified Web SDK, with per-version references and changelogs. Reports and Identity and Access Management used to be listed here and are now their own sections.","includeFiles":["**/platform/**"]}]}},"dynamicMarkdocComponents":[],"compilationErrors":[],"ast":{"$$mdtype":"Tag","name":"article","attributes":{},"children":[{"$$mdtype":"Tag","name":"Heading","attributes":{"level":1,"id":"webhooks","__idx":0},"children":["Webhooks"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"what-is-a-webhook","__idx":1},"children":["What is a Webhook?"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Webhooks allow your application to receive real-time notifications about events that occur in your account, such as payment link creation, payment completion, or payment failures."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Technically, a webhook is an ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["HTTP POST request"]}," that Glomo sends to a URL you control, containing a ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["JSON payload"]}," that describes what happened."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"what-is-a-webhook-url","__idx":2},"children":["What is a Webhook URL?"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A webhook URL is a publicly accessible HTTPS endpoint on your server that is set up to receive incoming HTTP POST requests from Glomo."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When you configure a webhook in the Glomo dashboard, you provide this URL. Every time an event occurs in your account, Glomo will send the event data to that URL."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Requirements for a valid webhook URL:"]}]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Must be publicly accessible over the internet (not ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["localhost"]},")"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Must use ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["HTTPS"]}," (plain HTTP is not accepted)"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Must respond with a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["200 OK"]}," status code upon successful receipt"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Must be able to handle POST requests with a JSON body"]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"how-does-a-webhook-url-work--step-by-step","__idx":3},"children":["How Does a Webhook URL Work? — Step by Step"]},{"$$mdtype":"Tag","name":"Steps","attributes":{"steps":["An event occurs (e.g. a customer completes a payment)","Glomo detects the event","Glomo sends an HTTP POST request to your Webhook URL","Your server receives the request and processes it","Your server responds with **HTTP 200 OK** to acknowledge receipt"]},"children":[]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"key-things-to-know","__idx":4},"children":["Key Things to Know"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Glomo sends webhook notifications as ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["HTTP POST requests"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["The data is delivered in ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["JSON format"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Every webhook payload always contains two important fields:",{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["entity_type"]}," — identifies ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["what"]}," the webhook is about (e.g. payment, order, payout)"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["event_type"]}," — identifies ",{"$$mdtype":"Tag","name":"em","attributes":{},"children":["what happened"]}," to it (e.g. success, failed, expired)"]}]}]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"example-payload-structure","__idx":5},"children":["Example Payload Structure"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Every webhook payload follows the same top-level structure:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"entity_type\": \"payment\",\n  \"event_type\": \"success\",\n  \"data\": {\n    \"id\": \"payt_686f7cc3pe69T\",\n    \"status\": \"success\",\n    ...\n  }\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Field"},"children":["Field"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Type"},"children":["Type"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Description"},"children":["Description"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["entity_type"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["string"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The type of resource this webhook is about (e.g., ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["payment"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["orders"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["payout"]},")."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["event_type"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["string"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The specific event that occurred on that resource (e.g., ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["success"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["failed"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["active"]},")."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["data"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["object"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["A full snapshot of the resource at the time the event occurred, including IDs, amounts, status, timestamps, and more."]}]}]}]}]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Note:"]}," This example is pretty-printed for readability. The body actually delivered is the RFC 8785 canonical form — one line, no whitespace, object keys sorted at every depth. That is also the form we sign, so verify against the raw body exactly as received. See ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#webhook-authentication--security"},"children":["Webhook Authentication & Security"]},"."]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"setting-up-webhooks","__idx":6},"children":["Setting Up Webhooks"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You can set up to receive webhook events on your account in 3 simple steps."]},{"$$mdtype":"Tag","name":"Steps","attributes":{"steps":["**Login** to your Glomo dashboard.","Navigate to **Settings > API & Webhooks.**","**Enter the URL** where you want to receive events, add your secret key, and click Create to add your webhook."]},"children":[]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"test-connection","__idx":7},"children":["Test Connection"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You can verify that your webhook URL is correctly set up and reachable by using the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Test Connection"]}," feature in the Glomo dashboard."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"img","attributes":{"src":"/assets/webhook-test-connection.9a00b664e14e5c76b7df55444e48f03640e0fc15b8fab96df1990e9f49b8cc8a.a02a8ec9.png","alt":"Webhook Test Connection"},"children":[]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"how-to-test-your-webhook-connection","__idx":8},"children":["How to Test Your Webhook Connection"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Save your webhook details first"]}," — Enter your Webhook URL and Secret, then click ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Save"]},". The Test Connection button is only available after the webhook details have been saved."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Click \"Test Connection\""]}," — The button is located in the top-right corner of the Webhook Edit page."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Glomo will send a test request to your configured webhook URL."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"possible-responses","__idx":9},"children":["Possible Responses"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Result"},"children":["Result"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Message"},"children":["Message"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["✅ Success"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Webhook connection successful"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["❌ Failure"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Request failed with status 422"]}]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If the test fails, verify that:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Your webhook URL is publicly accessible over HTTPS"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Your server is returning a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["200 OK"]}," response"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["There are no firewall or IP restrictions blocking Glomo's requests"]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"webhook-authentication--security","__idx":10},"children":["Webhook Authentication & Security"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Every webhook request Glomo sends is signed with your webhook secret key. This allows you to verify that the request genuinely came from Glomo and has not been tampered with in transit."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"how-signing-works","__idx":11},"children":["How Signing Works"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When Glomo sends a webhook, it:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Builds the JSON event object."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Serialises it using ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["RFC 8785 JSON Canonicalization Scheme (JCS)"]},". This produces one deterministic byte sequence — the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["canonical form"]},"."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Computes an ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["HMAC SHA-256"]}," over exactly those canonical bytes, using your webhook secret key, and hex-encodes the result."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Sends ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["those same canonical bytes"]}," as the HTTP request body, with the hex signature in the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["X-Glomopay-Signature"]}," header."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The signed bytes and the transmitted bytes are the same bytes. ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["The body you receive is already canonical"]},", so you never have to canonicalize anything yourself — you HMAC the raw request body exactly as it arrived."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The header value is 64 lowercase hexadecimal characters with no algorithm prefix — ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["X-Glomopay-Signature: d5b5…"]}," and not ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sha256=d5b5…"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"what-canonical-means","__idx":12},"children":["What \"canonical\" Means"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You do not need to implement JCS to verify a signature, but you do need to know what the bytes look like, because it explains why the body is not formatted the way the sample payloads in these docs are."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Under ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://www.rfc-editor.org/rfc/rfc8785.html"},"children":["RFC 8785"]},":"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Rule"},"children":["Rule"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"What Glomo sends"},"children":["What Glomo sends"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Key order"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Object keys are sorted lexicographically, recursively, at every nesting depth. ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["data"]}," comes before ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["entity_type"]}," before ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["event_type"]},"."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Array order"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Preserved exactly as generated. Sorting applies to object keys only, never to array elements."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Whitespace"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["None. No spaces after ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[":"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":[","]},", no newlines, no indentation."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Encoding"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["UTF-8. Non-ASCII characters are sent literally (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["café"]},", not ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["caf\\u00e9"]},")."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["String escaping"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Only the escapes JSON requires — ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\\\""]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\\\\"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\\b"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\\f"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\\n"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\\r"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\\t"]},", and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\\u00XX"]}," for other control characters. Forward slashes are ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["not"]}," escaped."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Numbers"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["ECMAScript ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["Number::toString"]}," form — ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["250000"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["1.5"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["0"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["0.000001"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["1e+21"]},". No trailing zeros; integers below 1e21 carry no decimal point and no exponent."]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"verifying-the-signature","__idx":13},"children":["Verifying the Signature"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["On your server:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Read the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["raw request body"]},", as bytes, before any JSON parsing."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Compute the HMAC SHA-256 of those bytes using your stored webhook secret key, and hex-encode it."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Compare your value with the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["X-Glomopay-Signature"]}," header using a ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["constant-time"]}," comparison."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["If they match, the webhook is authentic. If they don't, discard the request."]}]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Do not re-serialise the body before hashing."]}," Parsing the JSON and dumping it again — ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["json.dumps(json.loads(body))"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["JSON.stringify(req.body)"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["payload.to_json"]}," — produces different bytes (different spacing, different escaping, and in most languages a different key order), and the signature will never match. Hash the bytes you received."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Most web frameworks parse the body for you and discard the original bytes. Keep a handle on the raw body:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Framework"},"children":["Framework"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Raw body"},"children":["Raw body"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Rails"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["request.raw_post"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Django"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["request.body"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Flask"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["request.get_data()"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Express"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["express.raw({ type: 'application/json' })"]},", or the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["verify"]}," callback on ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["express.json()"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["PHP"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["file_get_contents('php://input')"]}]}]}]}]}]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Important:"]}," Never process a webhook payload without first verifying the signature. Skipping this step exposes your system to spoofed or tampered requests."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"worked-example","__idx":14},"children":["Worked Example"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Given the webhook secret ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["whsec_2f1c9b7a4d6e8031"]},", this is the exact request body Glomo puts on the wire — one line, 223 bytes, keys sorted at both levels:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"{\"data\":{\"amount\":250000,\"beneficiary\":{\"country\":\"DEU\",\"name\":\"Acme GmbH\"},\"created_at\":\"2026-09-09T07:00:09Z\",\"currency\":\"USD\",\"id\":\"payout_6a8fd80eo6coZ\",\"status\":\"success\"},\"entity_type\":\"payout\",\"event_type\":\"success\"}\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["HMAC SHA-256 of those bytes with that secret is:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"d5b5a68b9729f41c4a46f1e94c750bf10fb32f345a5467cbfec7739df83f2c02\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["which is what arrives in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["X-Glomopay-Signature"]},". Run your verification code against this pair before you point it at live traffic — if it reproduces that hex string, your raw-body handling is correct."]},{"$$mdtype":"Tag","name":"details","attributes":{},"children":[{"$$mdtype":"Tag","name":"summary","attributes":{},"children":["Code Examples"]},{"$$mdtype":"Tag","name":"details","attributes":{},"children":[{"$$mdtype":"Tag","name":"summary","attributes":{},"children":["PHP"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"php","header":{"controls":{"copy":{}}},"source":"// $payload must be the raw body: file_get_contents('php://input')\nfunction isValidSignature($payload, $headerSignature, $secretKey) {\n    $computedHash = hash_hmac('sha256', $payload, $secretKey);\n    return hash_equals($computedHash, $headerSignature);\n}\n","lang":"php"},"children":[]}]},{"$$mdtype":"Tag","name":"details","attributes":{},"children":[{"$$mdtype":"Tag","name":"summary","attributes":{},"children":["Ruby"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"ruby","header":{"controls":{"copy":{}}},"source":"require 'openssl'\n\n# payload must be the raw body: request.raw_post\ndef valid_signature?(payload, header_signature, secret_key)\n  computed_hash = OpenSSL::HMAC.hexdigest('sha256', secret_key, payload)\n  OpenSSL.secure_compare(computed_hash, header_signature.to_s)\nend\n","lang":"ruby"},"children":[]}]},{"$$mdtype":"Tag","name":"details","attributes":{},"children":[{"$$mdtype":"Tag","name":"summary","attributes":{},"children":["Node.js"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"javascript","header":{"controls":{"copy":{}}},"source":"const crypto = require('crypto');\n\n// payload must be the raw body Buffer, not the parsed object\nfunction isValidSignature(payload, headerSignature, secretKey) {\n    const computedHash = crypto.createHmac('sha256', secretKey).update(payload).digest('hex');\n    const a = Buffer.from(computedHash, 'utf8');\n    const b = Buffer.from(String(headerSignature), 'utf8');\n    return a.length === b.length && crypto.timingSafeEqual(a, b);\n}\n","lang":"javascript"},"children":[]}]},{"$$mdtype":"Tag","name":"details","attributes":{},"children":[{"$$mdtype":"Tag","name":"summary","attributes":{},"children":["Python"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"import hmac\nimport hashlib\n\n# payload must be the raw body bytes: request.body / request.get_data()\ndef is_valid_signature(payload, header_signature, secret_key):\n    computed_hash = hmac.new(secret_key.encode(), payload, hashlib.sha256).hexdigest()\n    return hmac.compare_digest(computed_hash, header_signature)\n","lang":"python"},"children":[]}]}]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Note:"]}," The ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Webhook delivery log"]}," in the dashboard stores the payload as parsed JSON and re-renders it for display, so the body shown there is not byte-identical to what was sent. Use it to inspect event contents, not to re-check a signature."]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"what-happens-if-your-server-doesnt-respond","__idx":15},"children":["What Happens if Your Server Doesn't Respond?"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If Glomo sends a webhook to your URL and does not receive a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["200 OK"]}," response — whether because your server is down, too slow, returned an error, or responded with any other status code — the delivery is considered failed."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Glomo will ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["automatically retry"]}," the webhook delivery up to ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["9 times"]},", using the following back-off schedule:"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Attempt"},"children":["Attempt"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Delay After Previous Attempt"},"children":["Delay After Previous Attempt"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["1st retry"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["1 minute"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["2nd retry"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["5 minutes"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["3rd retry"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["15 minutes"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["4th retry"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["1 hour"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["5th retry"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["3 hours"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["6th retry"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["6 hours"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["7th retry"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["12 hours"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["8th retry"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["24 hours"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["9th retry"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["48 hours"]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If all 9 retries are exhausted without a successful ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["200 OK"]},", no further delivery attempts will be made for that event."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"best-practices-to-avoid-missed-webhooks","__idx":16},"children":["Best Practices to Avoid Missed Webhooks"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Return ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["200 OK"]}," immediately"]}," — Acknowledge receipt first, then handle processing asynchronously in a background job or queue."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Monitor your endpoint"]}," — Set up alerting for 5xx errors or high response times on your webhook endpoint."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Design for idempotency"]}," — Since retries can result in the same event being delivered multiple times, ensure your processing logic handles duplicate deliveries safely."]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"glossary","__idx":17},"children":["Glossary"]},{"$$mdtype":"Tag","name":"div","attributes":{"className":"md-table-wrapper"},"children":[{"$$mdtype":"Tag","name":"table","attributes":{"className":"md"},"children":[{"$$mdtype":"Tag","name":"thead","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Term"},"children":["Term"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Definition"},"children":["Definition"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Webhook"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["An automated HTTP POST request sent by Glomo to your server when a specific event occurs."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Webhook URL"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["A publicly accessible HTTPS endpoint on your server configured to receive webhook POST requests from Glomo."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Payload"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The JSON body of the webhook request, containing details about the event that occurred."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["entity_type"]}]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["A field in every webhook payload that identifies the type of resource the event relates to (e.g., ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["payment"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["payout"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["orders"]},")."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["event_type"]}]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["A field in every webhook payload that identifies what happened to the resource (e.g., ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["success"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["failed"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["active"]},")."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["HMAC SHA-256"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["A cryptographic algorithm used to sign webhook payloads. Glomo signs each request; you verify the signature to confirm authenticity."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["X-Glomopay-Signature"]}]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The HTTP request header that carries the HMAC SHA-256 signature of the webhook payload, as 64 lowercase hex characters with no algorithm prefix."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["RFC 8785"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The JSON Canonicalization Scheme (JCS) — one deterministic serialisation of a JSON value, with object keys sorted at every depth and no insignificant whitespace. Glomo both signs and sends this form, so you verify against the raw body as received."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Idempotency"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The property of an operation that produces the same result even if applied multiple times. Your webhook handler should be idempotent to handle potential duplicate deliveries safely."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Retry"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["An automatic re-delivery of a webhook that did not receive a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["200 OK"]}," response. Glomo retries up to 9 times with increasing back-off intervals."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Back-off"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The strategy of waiting progressively longer between retry attempts, reducing load on a struggling server while ensuring eventual delivery."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Secret Key"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["A shared secret you configure when setting up a webhook. Glomo uses it to sign payloads; you use it to verify the signature."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Polling"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The alternative to webhooks — repeatedly querying an API to check for updates. Webhooks eliminate the need for polling by pushing updates proactively."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Smallest currency unit"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The convention used for all monetary amounts in Glomo webhooks. Amounts are expressed without decimal points (e.g., $10.00 is sent as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["1000"]},")."]}]}]}]}]}]},"headings":[{"value":"Webhooks","id":"webhooks","depth":1},{"value":"What is a Webhook?","id":"what-is-a-webhook","depth":2},{"value":"What is a Webhook URL?","id":"what-is-a-webhook-url","depth":2},{"value":"How Does a Webhook URL Work? — Step by Step","id":"how-does-a-webhook-url-work--step-by-step","depth":2},{"value":"Key Things to Know","id":"key-things-to-know","depth":2},{"value":"Example Payload Structure","id":"example-payload-structure","depth":2},{"value":"Setting Up Webhooks","id":"setting-up-webhooks","depth":2},{"value":"Test Connection","id":"test-connection","depth":2},{"value":"How to Test Your Webhook Connection","id":"how-to-test-your-webhook-connection","depth":3},{"value":"Possible Responses","id":"possible-responses","depth":3},{"value":"Webhook Authentication & Security","id":"webhook-authentication--security","depth":2},{"value":"How Signing Works","id":"how-signing-works","depth":3},{"value":"What \"canonical\" Means","id":"what-canonical-means","depth":3},{"value":"Verifying the Signature","id":"verifying-the-signature","depth":3},{"value":"Worked Example","id":"worked-example","depth":3},{"value":"What Happens if Your Server Doesn't Respond?","id":"what-happens-if-your-server-doesnt-respond","depth":2},{"value":"Best Practices to Avoid Missed Webhooks","id":"best-practices-to-avoid-missed-webhooks","depth":3},{"value":"Glossary","id":"glossary","depth":2}],"frontmatter":{"steps":["**Login** to your Glomo dashboard.","Navigate to **Settings > API & Webhooks.**","**Enter the URL** where you want to receive events, add your secret key, and click Create to add your webhook."],"flow":["An event occurs (e.g. a customer completes a payment)","Glomo detects the event","Glomo sends an HTTP POST request to your Webhook URL","Your server receives the request and processes it","Your server responds with **HTTP 200 OK** to acknowledge receipt"],"seo":{"title":"Webhooks"}},"lastModified":"2026-09-22T07:04:41.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/platform/webhooks","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}