{"templateId":"markdown","sharedDataIds":{"sidebar":"sidebar-@l10n/en-US/apis/sidebars.yaml"},"props":{"metadata":{"markdoc":{"tagList":["code-group"]},"type":"markdown"},"seo":{"title":"Webhooks","description":"Documentação oficial da API da plataforma de crédito embarcado Dinie. Integre produtos de crédito diretamente na sua aplicação.","siteUrl":"https://docs.dinie.com","lang":"pt-BR","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":["Webhooks deliver real-time notifications when resources change. Instead of polling the API, register a URL and receive ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["POST"]}," requests whenever events occur. The Dinie API follows the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://www.standardwebhooks.com/"},"children":["Standard Webhooks"]}," specification."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"configuring-webhook-endpoints","__idx":1},"children":["Configuring Webhook Endpoints"]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"create-an-endpoint","__idx":2},"children":["Create an Endpoint"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Register a URL to receive webhook events. You can subscribe to specific events or receive all of them."]},{"$$mdtype":"Tag","name":"CodeGroup","attributes":{"mode":"tabs"},"children":[{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","data-title":"Node.js","header":{"title":"Node.js","controls":{"copy":{}}},"source":"const endpoint = await dinie.webhooks.endpoints.create({\n  url: \"https://yourapp.example.com/webhooks/dinie\",\n  events: [\"customer.active\", \"credit_offer.available\", \"loan.*\"],\n  description: \"Production webhook\",\n});\n\n// Store endpoint.secret securely -- it is only displayed once.\nconsole.log(endpoint.secret); // \"whsec_xxxxx...\"\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"ruby","data-title":"Ruby","header":{"title":"Ruby","controls":{"copy":{}}},"source":"endpoint = dinie.webhooks.endpoints.create(\n  url: \"https://yourapp.example.com/webhooks/dinie\",\n  events: [\"customer.active\", \"credit_offer.available\", \"loan.*\"],\n  description: \"Production webhook\"\n)\n\n# Store endpoint.secret securely -- it is only displayed once.\nputs endpoint.secret  # \"whsec_xxxxx...\"\n","lang":"ruby"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","data-title":"Python","header":{"title":"Python","controls":{"copy":{}}},"source":"endpoint = dinie.webhooks.endpoints.create(\n    url=\"https://yourapp.example.com/webhooks/dinie\",\n    events=[\"customer.active\", \"credit_offer.available\", \"loan.*\"],\n    description=\"Production webhook\",\n)\n\n# Store endpoint.secret securely -- it is only displayed once.\nprint(endpoint.secret)  # \"whsec_xxxxx...\"\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","data-title":"cURL","header":{"title":"cURL","controls":{"copy":{}}},"source":"curl -X POST https://sandbox.api.dinie.com.br/v3/webhooks/endpoints \\\n  -H \"Authorization: Bearer dinie_at_...\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"url\": \"https://yourapp.example.com/webhooks/dinie\",\n    \"events\": [\"customer.active\", \"credit_offer.available\", \"loan.*\"],\n    \"description\": \"Production webhook\"\n  }'\n","lang":"bash"},"children":[]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The returned ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["endpoint.secret"]}," is required to verify signatures -- store it immediately in your secrets manager."]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Warning:"]}," The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["secret"]}," is ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["only returned on creation and rotation"]},". Store it immediately in your secrets manager. You need it to verify webhook signatures."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"managing-endpoints","__idx":3},"children":["Managing Endpoints"]},{"$$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":"Operation"},"children":["Operation"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Method"},"children":["Method"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Endpoint"},"children":["Endpoint"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["List all"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["GET"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/v3/webhooks/endpoints"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Get one"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["GET"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/v3/webhooks/endpoints/{id}"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Update"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PATCH"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/v3/webhooks/endpoints/{id}"]}]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Delete"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["DELETE"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["/v3/webhooks/endpoints/{id}"]}]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["You can disable an endpoint without deleting it by setting ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["status: \"disabled\""]}," via ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["PATCH"]},"."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"receiving-webhooks","__idx":4},"children":["Receiving Webhooks"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Each delivery is a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["POST"]}," request with authentication headers (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["webhook-id"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["webhook-timestamp"]},", ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["webhook-signature"]},"). Use the SDK to verify and parse the event:"]},{"$$mdtype":"Tag","name":"CodeGroup","attributes":{"mode":"tabs"},"children":[{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"typescript","data-title":"Node.js","header":{"title":"Node.js","controls":{"copy":{}}},"source":"import express from \"express\";\nimport Dinie from \"dinie\";\n\nconst app = express();\nconst dinie = new Dinie({\n  clientId: process.env.DINIE_CLIENT_ID,\n  clientSecret: process.env.DINIE_CLIENT_SECRET,\n  webhookSecret: process.env.DINIE_WEBHOOK_SECRET,\n});\n\napp.post(\"/webhooks/dinie\", express.raw({ type: \"application/json\" }), (req, res) => {\n  const event = dinie.webhooks.unwrap(req.body.toString(), req.headers);\n\n  switch (event.type) {\n    case \"customer.active\":\n      console.log(`Customer ${event.data.id} approved`);\n      break;\n    case \"loan.active\":\n      console.log(`Loan ${event.data.id} disbursed`);\n      break;\n  }\n\n  res.sendStatus(200);\n});\n","lang":"typescript"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"ruby","data-title":"Ruby","header":{"title":"Ruby","controls":{"copy":{}}},"source":"class WebhooksController < ApplicationController\n  skip_before_action :verify_authenticity_token\n\n  def create\n    event = dinie.webhooks.unwrap(request.raw_post, request.headers)\n\n    case event.type\n    when \"customer.active\"\n      puts \"Customer #{event.data.id} approved\"\n    when \"loan.active\"\n      puts \"Loan #{event.data.id} disbursed\"\n    end\n\n    head :ok\n  end\nend\n","lang":"ruby"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"python","data-title":"Python","header":{"title":"Python","controls":{"copy":{}}},"source":"import os\nfrom flask import Flask, request\nfrom dinie import Dinie\n\napp = Flask(__name__)\ndinie = Dinie(\n    client_id=os.environ[\"DINIE_CLIENT_ID\"],\n    client_secret=os.environ[\"DINIE_CLIENT_SECRET\"],\n    webhook_secret=os.environ[\"DINIE_WEBHOOK_SECRET\"],\n)\n\n@app.route(\"/webhooks/dinie\", methods=[\"POST\"])\ndef handle_webhook():\n    event = dinie.webhooks.unwrap(\n        request.get_data(as_text=True),\n        request.headers,\n    )\n\n    if event.type == \"customer.active\":\n        print(f\"Customer {event.data.id} approved\")\n    elif event.type == \"loan.active\":\n        print(f\"Loan {event.data.id} disbursed\")\n\n    return \"\", 200\n","lang":"python"},"children":[]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","data-title":"cURL","header":{"title":"cURL","controls":{"copy":{}}},"source":"# Test your endpoint by sending a simulated webhook payload:\ncurl -X POST https://yourapp.example.com/webhooks/dinie \\\n  -H \"Content-Type: application/json\" \\\n  -H \"webhook-id: msg_550e8400e29b41d4a716446655440000\" \\\n  -H \"webhook-timestamp: 1709546400\" \\\n  -H \"webhook-signature: v1,K5oZfzN95Z2P2rk2kbX1XISvnJtgBXMiGMKQ0iR1sLA=\" \\\n  -d '{\n    \"type\": \"customer.active\",\n    \"timestamp\": 1709546400,\n    \"data\": {\n      \"id\": \"cust_550e8400...\",\n      \"status\": \"active\"\n    }\n  }'\n","lang":"bash"},"children":[]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["webhook_secret"]}," is configured in the client constructor. The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["unwrap"]}," method uses this secret to verify the HMAC-SHA256 signature, validate the timestamp (5-minute replay protection), and return the typed event. If verification fails, an exception is thrown -- return ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["400"]}," in that case."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"manual-signature-verification","__idx":5},"children":["Manual Signature Verification"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["If you are not using the SDKs, implement verification manually following the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://www.standardwebhooks.com/"},"children":["Standard Webhooks"]}," specification:"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Build the signed content: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["{webhook-id}.{webhook-timestamp}.{raw_body}"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Decode the secret: remove the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["whsec_"]}," prefix and base64-decode the remainder"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Compute the HMAC-SHA256 of the signed content using the decoded secret"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Base64-encode the result and compare it against each signature in the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["webhook-signature"]}," header"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Reject if the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["webhook-timestamp"]}," is older than 5 minutes (replay protection)"]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["See the ",{"$$mdtype":"Tag","name":"MarkdownLink","attributes":{"href":"https://www.standardwebhooks.com/"},"children":["Standard Webhooks"]}," specification for full details on headers and signature format."]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"response-codes","__idx":6},"children":["Response Codes"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Your webhook endpoint must respond within ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["15 seconds"]},". Only the HTTP status code matters -- the response body is ignored."]},{"$$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":"Status"},"children":["Status"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Behavior"},"children":["Behavior"]},{"$$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":"strong","attributes":{},"children":["2xx"]}," (200-299)"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Success"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Delivery confirmed. Most common: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["200 OK"]}," or ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["204 No Content"]},"."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["3xx"]}," (300-399)"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Failure + retry"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Redirects are NOT followed. Update the endpoint URL via API instead."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["410"]}," Gone"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Endpoint disabled"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Dinie permanently disables the endpoint and stops sending events. Use to unsubscribe without calling the API."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["429"]}," Too Many Requests"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Throttle"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Dinie reduces send rate and reschedules with backoff."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["4xx"]}," (other)"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Failure + retry"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Any other client error triggers retries."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["502"]}," Bad Gateway"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Throttle"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Dinie reduces send rate before retrying."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["504"]}," Gateway Timeout"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Throttle"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Same behavior as 502."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["5xx"]}," (other)"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Failure + retry"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Any other server error triggers retries."]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Timeout (no response in 15s)"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Failure + retry"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Connection closed, delivery treated as failure."]}]}]}]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"retry-policy","__idx":7},"children":["Retry Policy"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Failed deliveries are retried with exponential backoff. Random jitter is added to each interval to prevent thundering herd."]},{"$$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":"Interval"},"children":["Interval"]}]}]},{"$$mdtype":"Tag","name":"tbody","attributes":{},"children":[{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["1"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Immediate"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["2"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["5 seconds"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["3"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["5 minutes"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["4"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["30 minutes"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["5"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["2 hours"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["6"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["5 hours"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["7"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["10 hours"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["8"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["14 hours"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["9"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["20 hours"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":["10"]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["24 hours"]}]}]}]}]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["After 10 failed attempts (~3 days), the event is marked as ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["failed"]},". Failed events can be resent manually. Repeated failures across multiple events will automatically disable the webhook endpoint."]},{"$$mdtype":"Tag","name":"blockquote","attributes":{},"children":[{"$$mdtype":"Tag","name":"p","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Info:"]}," Use the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["webhook-id"]}," header as an idempotency key to deduplicate deliveries on your side. The same event may be delivered more than once during retries."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"secret-rotation","__idx":8},"children":["Secret Rotation"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["Rotate your webhook signing secret without downtime:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"bash","header":{"controls":{"copy":{}}},"source":"curl -X POST https://sandbox.api.dinie.com.br/v3/webhooks/endpoints/we_550e8400.../secret/rotate \\\n  -H \"Authorization: Bearer dinie_at_...\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{ \"expire_current_in\": 3600 }'\n","lang":"bash"},"children":[]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["During the transition period (",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["expire_current_in"]},", default 1 hour, maximum 24 hours), both secrets -- old and new -- are active. The ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["webhook-signature"]}," header contains signatures for ",{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["both secrets"]},", so your verification code will match either one."]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Call ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["POST /v3/webhooks/endpoints/{id}/secret/rotate"]}," with a transition period"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Update your application with the new secret from the response"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Deploy -- during the transition period, both secrets work"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["After the transition period, only the new secret is used"]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"best-practices","__idx":9},"children":["Best Practices"]},{"$$mdtype":"Tag","name":"ol","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Always verify signatures"]}," -- never trust webhook payloads without verification"]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Respond quickly"]}," -- return a ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["200"]}," status within 15 seconds. Process the event asynchronously if needed."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Implement idempotency"]}," -- use the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["webhook-id"]}," to deduplicate. Your handler should safely process the same event twice."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Use HTTPS"]}," -- webhook URLs must use HTTPS. HTTP endpoints are rejected."]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Log deliveries"]}," -- store the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["webhook-id"]}," and ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["webhook-timestamp"]}," for debugging and audit trails."]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":2,"id":"available-events","__idx":10},"children":["Available Events"]},{"$$mdtype":"Tag","name":"p","attributes":{},"children":["All payloads follow the same envelope:"]},{"$$mdtype":"Tag","name":"CodeBlock","attributes":{"data-language":"json","header":{"controls":{"copy":{}}},"source":"{\n  \"type\": \"resource.action\",\n  \"timestamp\": 1709546400,\n  \"data\": { ... }\n}\n","lang":"json"},"children":[]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"wildcard-subscription","__idx":11},"children":["Wildcard Subscription"]},{"$$mdtype":"Tag","name":"ul","attributes":{},"children":[{"$$mdtype":"Tag","name":"li","attributes":{},"children":["Specific events: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["[\"customer.active\", \"loan.created\"]"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["All from a resource: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["[\"loan.*\"]"]}]},{"$$mdtype":"Tag","name":"li","attributes":{},"children":["All events: omit the ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["events"]}," field or pass an empty array"]}]},{"$$mdtype":"Tag","name":"Heading","attributes":{"level":3,"id":"events-by-resource","__idx":12},"children":["Events by Resource"]},{"$$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":"Resource"},"children":["Resource"]},{"$$mdtype":"Tag","name":"th","attributes":{"data-label":"Event"},"children":["Event"]},{"$$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":"strong","attributes":{},"children":["Customer"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["customer.created"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["New customer registered (status: ",{"$$mdtype":"Tag","name":"code","attributes":{},"children":["creating"]},")"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["customer.under_review"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["KYC submitted, review started"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["customer.kyc_updated"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["KYC requirement status changed"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["customer.active"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Fully verified, registration complete"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["customer.denied"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Permanently blocked (fraud, closed company)"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Credit Offer"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["credit_offer.available"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["New offer created for the customer"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["credit_offer.expired"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Offer passed its expiration date"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"strong","attributes":{},"children":["Loan"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["loan.created"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Loan created with CCB, awaiting signatures"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["loan.signature_received"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Individual signer completed their signature"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["loan.processing"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["All signatures collected, disbursement in progress"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["loan.active"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Funds disbursed, repayment started"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["loan.payment_received"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Installment received"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["loan.finished"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Loan fully repaid"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["loan.cancelled"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Loan cancelled"]}]},{"$$mdtype":"Tag","name":"tr","attributes":{},"children":[{"$$mdtype":"Tag","name":"td","attributes":{},"children":[]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":[{"$$mdtype":"Tag","name":"code","attributes":{},"children":["loan.error"]}]},{"$$mdtype":"Tag","name":"td","attributes":{},"children":["Processing or disbursement error"]}]}]}]}]}]},"headings":[{"value":"Webhooks","id":"webhooks","depth":1},{"value":"Configuring Webhook Endpoints","id":"configuring-webhook-endpoints","depth":2},{"value":"Create an Endpoint","id":"create-an-endpoint","depth":3},{"value":"Managing Endpoints","id":"managing-endpoints","depth":3},{"value":"Receiving Webhooks","id":"receiving-webhooks","depth":2},{"value":"Manual Signature Verification","id":"manual-signature-verification","depth":2},{"value":"Response Codes","id":"response-codes","depth":2},{"value":"Retry Policy","id":"retry-policy","depth":2},{"value":"Secret Rotation","id":"secret-rotation","depth":2},{"value":"Best Practices","id":"best-practices","depth":2},{"value":"Available Events","id":"available-events","depth":2},{"value":"Wildcard Subscription","id":"wildcard-subscription","depth":3},{"value":"Events by Resource","id":"events-by-resource","depth":3}],"frontmatter":{"seo":{"title":"Webhooks"}},"lastModified":"2026-03-19T21:33:27.000Z","pagePropGetterError":{"message":"","name":""}},"slug":"/en-us/apis/concepts/webhooks","userData":{"isAuthenticated":false,"teams":["anonymous"]},"isPublic":true}