{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-@l10n/en/simpay-flow/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["admonition","tabs","tab"]},"type":"markdown"},"seo":{"title":"Webhooks","description":"Znajdziesz tutaj wszystkie niezbędne informacje dotyczące integracji i obsługi systemu płatności SimPay.pl","siteUrl":"https://docs.simpay.pl","llmstxt":{"hide":false,"sections":[{"title":"Table of contents","includeFiles":["**/*"],"excludeFiles":[]}],"excludeFiles":[]}},"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":"p","attributes":{},"children":["SimPay Flow uses two independent kinds of notifications:"]},{"$$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":"Kind"},"children":["Kind"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"What it covers"},"children":["What it covers"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Default URL and key"},"children":["Default URL and key"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Flow webhooks"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Status changes of submerchants, their payment channels and withdrawals"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["URL and key set in the Flow service in the SimPay Partner Panel"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Payment webhooks (IPN)"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Submerchant transactions, refunds, BLIK codes, subscriptions and BLIK aliases - ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/notifications/payment"},"children":["standard IPN notifications"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Parent service IPN URL and key"]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You can override both URLs for each submerchant - see ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#custom-urls-for-a-submerchant"},"children":["Custom URLs for a submerchant"]},"."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"flow-webhooks","__idx":1},"children":["Flow webhooks"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"notification-structure","__idx":2},"children":["Notification structure"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Flow webhooks are sent with the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["POST"]}," method as JSON, with the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["User-Agent: SimPay-IPN/2.0"]}," header."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"type\": \"flow_submerchant_withdraw:status_changed\",\n  \"webhook_id\": \"0199a6c2-5f3e-7d21-8b4a-2c3d4e5f6a7b\",\n  \"attempt\": 0,\n  \"flow_service\": {\n    \"id\": \"flow_aaaabbbb\",\n    \"name\": \"My platform\"\n  },\n  \"date\": \"2026-10-01T12:00:00+02:00\",\n  \"data\": {\n    \"id\": \"0199a5f0-2b8c-7c1e-9e3a-1f2b3c4d5e6f\",\n    \"short_id\": \"1f2b3c4d\",\n    \"amount\": 150.5,\n    \"created_at\": \"2026-10-01T10:00:00+02:00\",\n    \"updated_at\": \"2026-10-01T12:00:00+02:00\",\n    \"wallet\": {\n      \"id\": \"0199a5f0-1111-7c1e-9e3a-1f2b3c4d5e6f\",\n      \"currency\": \"PLN\"\n    },\n    \"status\": {\n      \"previous\": \"accepted\",\n      \"current\": \"done\"\n    },\n    \"submerchant\": {\n      \"id\": \"flsm_aaaabbbb\",\n      \"custom_id\": \"user-123\"\n    }\n  },\n  \"signature\": \"f54b85c687f2f4dd6f554350561bf9eb970bc51f0c5bbd1bf4b5172ad07f9f4e\"\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":"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":["type"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Event type (see below)"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["webhook_id"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Unique notification ID (UUID). Each retry attempt has a new ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["webhook_id"]},"."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["attempt"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Delivery attempt number, starting from ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["0"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["flow_service.id"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Flow service ID"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["flow_service.name"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Flow service name"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["date"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Notification send date (ISO 8601)"]}]},{"$$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":["Event data - depends on ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["type"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["signature"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["SHA-256 signature - see ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#signature-verification"},"children":["Signature verification"]}]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"response","__idx":3},"children":["Response"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["A notification is considered delivered when your server responds with:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["code ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["200"]}," with the body ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["OK"]},", or"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["code ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["202"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["204"]},"."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Any other response, connection error or timeout (5 s to connect, 15 s for the response) means a failed attempt."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"retries","__idx":4},"children":["Retries"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Failed notifications are retried - up to ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["30 attempts"]},":"]},{"$$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":"Attempts"},"children":["Attempts"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Interval between attempts"},"children":["Interval between attempts"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["1-10"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["approx. 1 minute"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["11-15"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["approx. 5 minutes"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["16-20"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["approx. 30 minutes"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["21-30"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["approx. 1 hour"]}]}]}]}]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info","name":"Only the current state is sent"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If the object's state changes again before a retry (e.g. a withdrawal goes from ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["accepted"]}," to ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["done"]},"), the outdated notification is no longer sent - you'll only receive the notification about the latest change. Therefore always treat ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["current"]}," as the current state and don't assume you'll receive every intermediate change."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You'll find the history of sent webhooks (including your server's responses) in the SimPay Partner Panel, in the Flow service settings. You can also send a test notification from there."]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"flow-events","__idx":5},"children":["Flow events"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"flow_submerchantstatus_changed","__idx":6},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["flow_submerchant:status_changed"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The submerchant status has changed - e.g. after onboarding data is submitted, after SimPay approval or after the account is blocked."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"\"data\": {\n  \"submerchant\": {\n    \"id\": \"flsm_aaaabbbb\",\n    \"custom_id\": \"user-123\"\n  },\n  \"previous\": {\n    \"status\": \"client_verify\",\n    \"aml_level\": 1,\n    \"aml_status\": \"sent\",\n    \"fully_active\": false\n  },\n  \"current\": {\n    \"status\": \"client_active\",\n    \"aml_level\": 2,\n    \"aml_status\": \"accepted\",\n    \"fully_active\": true\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":"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":["status"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Submerchant account status at SimPay (e.g. ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["client_new"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["client_verify"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["client_active"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["client_block"]},")"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["aml_level"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["AML verification level (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["2"]}," - full verification)"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["aml_status"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["AML verification status (e.g. ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["sent"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["accepted"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["declined"]},")"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["fully_active"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["true"]}," when the submerchant is fully active and can accept payments"]}]}]}]}]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"info","name":"Tip"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To decide whether the submerchant can accept payments, use the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["current.fully_active"]}," field. The other fields are informational."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"flow_submerchant_payment_channelstatus_changed","__idx":7},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["flow_submerchant_payment_channel:status_changed"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The status of a submerchant payment channel has changed - e.g. a channel sent for verification was approved or rejected, the BLIK OneClick status changed, or the active BLIK Recurring Payments models changed."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"\"data\": {\n  \"submerchant\": {\n    \"id\": \"flsm_aaaabbbb\",\n    \"custom_id\": \"user-123\"\n  },\n  \"channel\": {\n    \"slug\": \"blik-level0\",\n    \"name\": \"BLIK Level 0\"\n  },\n  \"previous\": {\n    \"status\": \"active\",\n    \"blik_one_click_status\": \"REGISTERED_WAITING_ACTIVATION\",\n    \"blik_recurrent_a\": null,\n    \"blik_recurrent_m\": null,\n    \"blik_recurrent_o\": null\n  },\n  \"current\": {\n    \"status\": \"active\",\n    \"blik_one_click_status\": \"ACTIVE\",\n    \"blik_recurrent_a\": null,\n    \"blik_recurrent_m\": null,\n    \"blik_recurrent_o\": null\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":"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":["channel.slug"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Payment channel ID (same as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["id"]}," in ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/apis/flow/flowgetsubmerchantpaymentchannels"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["GET .../payment-channels"]}]},")"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["status"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Channel status: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["new"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["in_progress"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["active"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["rejected"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["blik_one_click_status"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["BLIK OneClick status (only ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["blik-level0"]},"): ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PENDING_REGISTRATION"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["REGISTERED_WAITING_ACTIVATION"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ACTIVE"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["REJECTED"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["null"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["blik_recurrent_a"]}," / ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["_m"]}," / ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["_o"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Whether the BLIK Recurring Payments model AUTO / MANUAL / OPEN is active (only ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["blik-recurrent"]},") or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["null"]}]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"flow_submerchant_withdrawstatus_changed","__idx":8},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["flow_submerchant_withdraw:status_changed"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The status of a submerchant withdrawal has changed. Example ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["data"]}," - see ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#notification-structure"},"children":["Notification structure"]},"."]},{"$$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":"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":["id"]}," / ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["short_id"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Withdrawal ID (UUID) and withdrawal short ID"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["amount"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Withdrawal amount"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["wallet.id"]}," / ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["wallet.currency"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Wallet the withdrawal is made from and its currency"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["status.previous"]}," / ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["status.current"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Previous and current withdrawal status: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["new"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["accepted"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["waiting"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["done"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["rejected"]}]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"flowtest","__idx":9},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["flow:test"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Test notification sent from the Partner Panel. ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["data"]}," contains only a random ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["nonce"]}," field:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"\"data\": {\n  \"nonce\": \"01K6FZ8X3Q9V2N7M4T5R1W0YAB\"\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"signature-verification","__idx":10},"children":["Signature verification"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Every Flow webhook contains the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["signature"]}," field - a ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["SHA-256"]}," hash (lowercase hex). The rule is the same as for ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/notifications/payment/signature"},"children":["payment notifications"]},":"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["take ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["all values"]}," from the notification (without the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["signature"]}," field) in the order they appear in the JSON, flattening nested objects,"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["join them with the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["|"]}," separator,"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["append the Flow webhook key at the end,"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["compute SHA-256 and compare it with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["signature"]}," using a timing-safe comparison function."]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"type|webhook_id|attempt|flow_service.id|flow_service.name|date|data_value1|...|data_valueN|FLOW_WEBHOOK_KEY\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"warning","name":"Booleans, null and numbers"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Unlike payment notifications, Flow webhooks contain ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["true"]},"/",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["false"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["null"]}," and numeric values. Before joining, convert them to text the way PHP does:"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["true"]}," → ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["1"]},","]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["false"]}," → empty string,"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["null"]}," → empty string,"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["numbers → without redundant zeros (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["0"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["2"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["150.5"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["100"]}," for ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["100.0"]},")."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["In JavaScript ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["String(true)"]}," gives ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\"true\""]},", and in Python ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["str(True)"]}," gives ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["\"True\""]}," - without the conversion the signature won't match."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Key:"]}," if the submerchant has a ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"#custom-urls-for-a-submerchant"},"children":["custom Flow webhook URL"]},", notifications about it are signed with its key. Otherwise - with the webhook key set in the Flow service."]},{"$$mdtype":"Tag","name":"details","attributes":{},"children":[{"$$mdtype":"Tag","name":"summary","attributes":{},"children":["See the signature calculation for the example above"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Key: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["FLOW_WEBHOOK_KEY_64"]}]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"text","header":{"controls":{"copy":{}}},"source":"flow_submerchant_withdraw:status_changed|0199a6c2-5f3e-7d21-8b4a-2c3d4e5f6a7b|0|flow_aaaabbbb|My platform|2026-10-01T12:00:00+02:00|0199a5f0-2b8c-7c1e-9e3a-1f2b3c4d5e6f|1f2b3c4d|150.5|2026-10-01T10:00:00+02:00|2026-10-01T12:00:00+02:00|0199a5f0-1111-7c1e-9e3a-1f2b3c4d5e6f|PLN|accepted|done|flsm_aaaabbbb|user-123|FLOW_WEBHOOK_KEY_64\n","lang":"text"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["SHA-256: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["f54b85c687f2f4dd6f554350561bf9eb970bc51f0c5bbd1bf4b5172ad07f9f4e"]}]}]},{"$$mdtype":"Tag","name":"Tabs","attributes":{"size":"medium"},"children":[{"$$mdtype":"Tag","name":"div","attributes":{"label":"PHP","disable":false},"children":[{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"php","header":{"controls":{"copy":{}}},"source":"<?php\n\nfunction simpayFlowSignatureValid(array $payload, string $key): bool\n{\n    $signature = $payload['signature'] ?? '';\n    unset($payload['signature']);\n\n    $values = [];\n    array_walk_recursive($payload, function ($value) use (&$values) {\n        $values[] = $value;\n    });\n    $values[] = $key;\n\n    // implode() converts true to \"1\", and false and null to an empty string\n    return hash_equals(hash('sha256', implode('|', $values)), $signature);\n}\n\n$payload = json_decode(file_get_contents('php://input'), true);\n\nif (!is_array($payload) || !simpayFlowSignatureValid($payload, 'FLOW_WEBHOOK_KEY')) {\n    http_response_code(403);\n    exit('INVALID_SIGNATURE');\n}\n\n// handle the $payload['type'] event\n\necho 'OK';\n","lang":"php"},"children":[]}]},{"$$mdtype":"Tag","name":"div","attributes":{"label":"Node","disable":false},"children":[{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"js","header":{"controls":{"copy":{}}},"source":"const crypto = require('crypto');\n\nfunction toPhpString(value) {\n    if (value === true) return '1';\n    if (value === false || value === null) return '';\n    return String(value);\n}\n\nfunction flatten(value, result = []) {\n    if (value !== null && typeof value === 'object') {\n        Object.values(value).forEach((item) => flatten(item, result));\n    } else {\n        result.push(toPhpString(value));\n    }\n    return result;\n}\n\nfunction simpayFlowSignatureValid(payload, key) {\n    const { signature, ...rest } = payload;\n    if (typeof signature !== 'string' || !/^[0-9a-f]{64}$/.test(signature)) {\n        return false;\n    }\n\n    const values = flatten(rest);\n    values.push(key);\n\n    const expected = crypto.createHash('sha256').update(values.join('|')).digest('hex');\n\n    return crypto.timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(signature, 'hex'));\n}\n\n// Express.js\napp.post('/webhooks/simpay/flow', express.json(), (req, res) => {\n    if (!simpayFlowSignatureValid(req.body, process.env.SIMPAY_FLOW_WEBHOOK_KEY)) {\n        return res.status(403).send('INVALID_SIGNATURE');\n    }\n\n    // handle the req.body.type event\n\n    res.send('OK');\n});\n","lang":"js"},"children":[]}]},{"$$mdtype":"Tag","name":"div","attributes":{"label":"Python","disable":false},"children":[{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","header":{"controls":{"copy":{}}},"source":"import hashlib\nimport hmac\n\n\ndef to_php_string(value) -> str:\n    if value is True:\n        return '1'\n    if value is False or value is None:\n        return ''\n    if isinstance(value, float) and value.is_integer():\n        return str(int(value))\n    return str(value)\n\n\ndef flatten(value, result):\n    if isinstance(value, dict):\n        for item in value.values():\n            flatten(item, result)\n    elif isinstance(value, list):\n        for item in value:\n            flatten(item, result)\n    else:\n        result.append(to_php_string(value))\n    return result\n\n\ndef simpay_flow_signature_valid(payload: dict, key: str) -> bool:\n    data = {k: v for k, v in payload.items() if k != 'signature'}\n    values = flatten(data, [])\n    values.append(key)\n\n    expected = hashlib.sha256('|'.join(values).encode()).hexdigest()\n\n    return hmac.compare_digest(expected, payload.get('signature', ''))\n","lang":"python"},"children":[]}]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"payment-webhooks-ipn-in-simpay-flow","__idx":11},"children":["Payment webhooks (IPN) in SimPay Flow"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Notifications about submerchant transactions, refunds, BLIK codes, subscriptions and BLIK aliases are ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/notifications/payment"},"children":["standard IPN notifications"]}," - with the same structure and the same ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/notifications/payment/signature"},"children":["signature calculation"]},". In SimPay Flow they differ in a few details."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"url-and-key","__idx":12},"children":["URL and key"]},{"$$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":"Situation"},"children":["Situation"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"URL"},"children":["URL"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Signature key"},"children":["Signature key"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The submerchant ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["doesn't have"]}," a custom IPN URL"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Parent service IPN URL"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Parent service IPN key"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["The submerchant ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["has"]}," a custom IPN URL"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Submerchant's custom URL"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Submerchant's IPN key (returned when the URL was set)"]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["This way, in the basic setup you handle notifications for all submerchants with a single endpoint and a single key - the same one you use for the parent service."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"service_id","__idx":13},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["service_id"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["In ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["all"]}," payment notifications related to submerchants (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["transaction:status_changed"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["transaction_refund:status_changed"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["transaction_blik_level0:code_status_changed"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["subscription:status_changed"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["blik:alias_status_changed"]},") the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["service_id"]}," field contains the ID of the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["parent service"]}," - the one you pass in the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/payment/{serviceId}/..."]}," path. The internal submerchant service never appears in notifications."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"the-dataflow-object","__idx":14},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["data.flow"]}," object"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Every payment notification related to a submerchant (all events except ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["ipn:test"]},") contains the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["flow"]}," object in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["data"]},". For regular Online Payments services the object is not present."]},{"$$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":"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":["flow.submerchant_id"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Submerchant ID (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["flsm_..."]},")"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["flow.submerchant_custom_id"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Submerchant ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["customId"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["flow.flow_service_id"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Flow service ID"]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Since ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["service_id"]}," points to the parent service, it's the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["flow"]}," object that lets you assign a transaction, refund, subscription or alias to the right submerchant in your system."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"\"flow\": {\n  \"submerchant_id\": \"flsm_aaaabbbb\",\n  \"submerchant_custom_id\": \"user-123\",\n  \"flow_service_id\": \"flow_aaaabbbb\"\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"changes-in-transactionstatus_changed","__idx":15},"children":["Changes in ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["transaction:status_changed"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Additionally, the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["transaction:status_changed"]}," event contains the ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["amount.commission_flow"]}]}," field - your Flow commission on the transaction (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["string"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["%.2f"]}," format). It's present only when the commission is greater than zero."]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"type\": \"transaction:status_changed\",\n  \"notification_id\": \"0199a6d0-1234-7abc-9def-0123456789ab\",\n  \"date\": \"2026-10-01T12:05:00+02:00\",\n  \"data\": {\n    \"id\": \"0199a6cf-aaaa-7bbb-8ccc-0123456789ab\",\n    \"payer_transaction_id\": \"Q68KLAKN\",\n    \"service_id\": \"252b85c9\",\n    \"status\": \"transaction_paid\",\n    \"amount\": {\n      \"final_currency\": \"PLN\",\n      \"final_value\": \"49.99\",\n      \"original_currency\": \"PLN\",\n      \"original_value\": \"49.99\",\n      \"commission_system\": \"0.60\",\n      \"commission_partner\": \"49.14\",\n      \"commission_currency\": \"PLN\",\n      \"commission_flow\": \"0.25\"\n    },\n    \"control\": \"order-1001\",\n    \"payment\": {\n      \"channel\": \"blik\",\n      \"type\": \"blik\"\n    },\n    \"customer\": {\n      \"country_code\": \"PL\"\n    },\n    \"paid_at\": \"2026-10-01T12:04:58+02:00\",\n    \"created_at\": \"2026-10-01T12:03:10+02:00\",\n    \"flow\": {\n      \"submerchant_id\": \"flsm_aaaabbbb\",\n      \"submerchant_custom_id\": \"user-123\",\n      \"flow_service_id\": \"flow_aaaabbbb\"\n    }\n  },\n  \"signature\": \"...\"\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"warning","name":"The signature covers the new fields"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["commission_flow"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["flow"]}," fields are included in the signature calculation like any other field of the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["data"]}," object. If your validator dynamically flattens the whole payload (as in the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/notifications/payment/signature"},"children":["examples"]},"), you don't need to change anything."]}]},{"$$mdtype":"Tag","name":"hr","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"custom-urls-for-a-submerchant","__idx":16},"children":["Custom URLs for a submerchant"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["By default, all notifications go to the URLs shared by the whole service. You can set separate URLs for a selected submerchant - e.g. when each of your Customers has its own application instance."]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You get the current settings with ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/apis/flow/flowgetsubmerchantwebhooks"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["GET .../submerchants/{submerchantId}/webhooks"]}]}," and change them with ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/apis/flow/flowupdatesubmerchantwebhooks"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PATCH .../submerchants/{submerchantId}/webhooks"]}]},":"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"curl -X PATCH \"https://api.simpay.pl/flow/{flowServiceId}/submerchants/user-123/webhooks\" \\\n  -H \"Authorization: Bearer YOUR_API_TOKEN\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"flow\": { \"customUrl\": \"https://customer-123.example.com/webhooks/flow\" },\n    \"payment\": { \"customUrl\": \"https://customer-123.example.com/webhooks/ipn\" }\n  }'\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"success\": true,\n  \"data\": {\n    \"paymentNewIpnKey\": \"aVeryLongAndSecureKeyFor32Chars0\",\n    \"flowNewIpnKey\": \"...\"\n  }\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"Admonition","attributes":{"type":"warning","name":"Save the keys from the response"},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":["When a custom URL is set for the first time, SimPay generates a ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["separate key"]}," for the submerchant to verify notification signatures. The key is returned ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["only once"]}," - in the response to this request. On subsequent URL changes the key doesn't change, and the response field is ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["null"]},"."]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["To go back to the default URL, send ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{\"flow\": {\"useDefault\": true}}"]}," (or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["payment"]},"). The submerchant's custom URL and key will be removed, and notifications will go back to the service URL and key."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"new-key","__idx":17},"children":["New key"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If the key leaked or you lost it, generate a new one with ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"/apis/flow/flowupdatesubmerchantwebhookkey"},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PATCH .../webhooks/key"]}]}," with ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{\"type\": \"flow\"}"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{\"type\": \"payment\"}"]},". This works only for submerchants with a custom webhook URL of the given type. After a new key is generated, the previous one is no longer used."]}]},"headings":[{"value":"Webhooks","id":"webhooks","depth":1},{"value":"Flow webhooks","id":"flow-webhooks","depth":2},{"value":"Notification structure","id":"notification-structure","depth":3},{"value":"Response","id":"response","depth":3},{"value":"Retries","id":"retries","depth":3},{"value":"Flow events","id":"flow-events","depth":2},{"value":"flow_submerchant:status_changed","id":"flow_submerchantstatus_changed","depth":3},{"value":"flow_submerchant_payment_channel:status_changed","id":"flow_submerchant_payment_channelstatus_changed","depth":3},{"value":"flow_submerchant_withdraw:status_changed","id":"flow_submerchant_withdrawstatus_changed","depth":3},{"value":"flow:test","id":"flowtest","depth":3},{"value":"Signature verification","id":"signature-verification","depth":2},{"value":"Payment webhooks (IPN) in SimPay Flow","id":"payment-webhooks-ipn-in-simpay-flow","depth":2},{"value":"URL and key","id":"url-and-key","depth":3},{"value":"service_id","id":"service_id","depth":3},{"value":"The data.flow object","id":"the-dataflow-object","depth":3},{"value":"Changes in transaction:status_changed","id":"changes-in-transactionstatus_changed","depth":3},{"value":"Custom URLs for a submerchant","id":"custom-urls-for-a-submerchant","depth":2},{"value":"New key","id":"new-key","depth":3}],"frontmatter":{"seo":{"title":"Webhooks"}},"lastModified":"2026-10-01T15:18:54.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/en/simpay-flow/webhooks","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}