---
title: End of Dunning
slug: end-of-dunning
description: Set up the End of Dunning webhook to receive updates on completed dunning processes. Learn how to handle webhook responses securely with Butter's signature validation.
docTags: 
createdAt: 2024-11-21T18:23:44.898Z
---

{
  "id": "eHxEf3keaNFrnrgN3eIOl",
  "type": "api-oas-v2",
  "data": {
    "method": "POST",
    "url": "https://api.<merchant-url>.com/webhooks/end-of-dunning",
    "servers": [
      {
        "url": "https://api.<merchant-url>.com/webhooks/end-of-dunning",
        "description": "An example of a url the merchant would create to receive these events."
      }
    ],
    "name": "End of Dunning",
    "description": "<p>Butter will send webhooks via HTTP POST requests to this endpoint when dunning is complete and we will no longer make attempts to recover the payment. Butter guarantees 'at least once' delivery of events, and provides a deduplication ID to enable integrators to handle duplicate event delivery.</p>\n<p>Note - The Butter webhook payload is a simple JSON object containing the attributes specified below. The payload does not contain a higher-level 'body' object that contains the other attributes.</p>\n<p>Add field values in the boxes below to create an example payload on the right side of the screen.</p>",
    "contentType": "application/json",
    "request": {
      "pathParameters": [],
      "headerParameters": [
        {
          "kind": "optional",
          "name": "X-Butter-Webhook-Type",
          "type": "string",
          "description": ""
        },
        {
          "kind": "optional",
          "name": "X-Butter-Webhook-Expiration",
          "type": "integer",
          "description": "The unix timestamp that the webhook signature expires at.",
          "default": 1698251668
        },
        {
          "kind": "optional",
          "name": "X-Butter-Webhook-Signature",
          "type": "string<hmac>",
          "description": "The signature the merchant can use for validating the security of the webhook.",
          "default": "06cf8a25ef73841fea0b20f13499f173e7767da53cccba2412b261816515f30d"
        },
        {
          "kind": "optional",
          "name": "X-Butter-Webhook-Deduplication-ID",
          "type": "string<md5>",
          "description": "A unique identifier that can be used to deduplicate the webhook.",
          "default": "47bce5c74f589f4867dbd57e9ca9f808"
        }
      ],
      "queryParameters": [],
      "bodyDataParameters": [
        {
          "kind": "required",
          "name": "body",
          "type": "object",
          "description": "A notification that the dunning period has elapsed for the specified invoice.",
          "customType": "end_of_dunning",
          "schema": [
            {
              "name": "timestamp",
              "kind": "optional",
              "type": "string",
              "description": "The time when this webhook was created and sent, not necessarily when dunning ended.",
              "default": "2023-10-25T03:57:26Z",
              "modelRef": "#/components/schemas/timestamp",
              "customType": "timestamp",
              "schema": []
            },
            {
              "name": "invoice_id",
              "kind": "optional",
              "type": "string",
              "description": "The ID of the invoice from the merchant's internal subscription management system.",
              "example": "",
              "default": "inv_12345"
            },
            {
              "name": "recovered",
              "kind": "optional",
              "type": "boolean",
              "description": "Boolean designating if the invoice was recovered.",
              "example": "",
              "default": true
            },
            {
              "name": "custom",
              "kind": "optional",
              "type": "object",
              "description": "Any custom fields the merchant passed in the initial failed payment POST to the Recover Enterprise API's \"Send Payment Information\" endpoint.",
              "example": "{\"brand\":\"DTC\"}",
              "schema": []
            }
          ],
          "modelRef": "#/components/schemas/end_of_dunning",
          "isExpanded": true
        }
      ],
      "formDataParameters": [],
      "oAuthParameters": [
        {
          "id": "basic_auth",
          "name": "basic_auth",
          "kind": "optional",
          "type": "http",
          "description": "<p>Basic auth credentials you (the merchant) provide to Butter to authorize webhook requests.</p>",
          "scheme": "basic"
        },
        {
          "id": "api_key_auth",
          "name": "x-api-key",
          "kind": "optional",
          "type": "apiKey",
          "description": "<p>API key credentials you (the merchant) provide to Butter to authorize requests to the Retry API.</p>",
          "in": "header"
        }
      ]
    },
    "responses": [
      {
        "statusCode": "200",
        "description": "Event Delivered Successfully",
        "jsonExample": "",
        "isExpanded": true,
        "schema": [
          {
            "kind": "optional",
            "type": "object",
            "description": "The webhook event was delivered successfully.",
            "customType": "inline_response_200",
            "schema": [
              {
                "name": "detail",
                "kind": "optional",
                "type": "string",
                "example": "Event delivered successfully",
                "default": "Event delivered successfully"
              }
            ],
            "modelRef": "#/components/schemas/inline_response_200",
            "isExpanded": true
          }
        ]
      },
      {
        "statusCode": "500",
        "description": "Event Not Delivered",
        "jsonExample": "",
        "isExpanded": true,
        "schema": [
          {
            "kind": "optional",
            "type": "object",
            "description": "The webhook event failed to deliver due to an internal server error. Webhooks will retry delivery every 12 hours for 7 days on failure.",
            "customType": "inline_response_500",
            "schema": [
              {
                "name": "detail",
                "kind": "optional",
                "type": "string",
                "example": "Server is unavailable",
                "default": "Server is unavailable"
              }
            ],
            "modelRef": "#/components/schemas/inline_response_500",
            "isExpanded": true
          }
        ]
      }
    ],
    "hasXCodeSamples": false,
    "examples": {
      "languages": [
        {
          "id": "csCoKHLhicOaz83WR3DM0",
          "language": "curl",
          "label": "cURL",
          "code": "curl --request POST \\\n     --url https://api.<merchant-url>.com/webhooks/end-of-dunning \\\n     --header 'accept: application/json' \\\n     --header 'content-type: application/json' \\\n     --header 'x-butter-webhook-expiration: 1698251668' \\\n     --header 'x-butter-webhook-signature: 06cf8a25ef73841fea0b20f13499f173e7767da53cccba2412b261816515f30d' \\\n     --header 'x-butter-webhook-deduplication-id: 47bce5c74f589f4867dbd57e9ca9f808' \\\n     --data '{\n     \"timestamp\": \"2023-10-25T03:57:26Z\",\n     \"invoice_id\": \"inv_12345\",\n     \"recovered\": true\n     }'"
        },
        {
          "id": "pRryQDa3QQCI8n0e5viCQ",
          "language": "javascript",
          "label": "javascript",
          "code": "var myHeaders = new Headers();\nmyHeaders.append(\"accept\", \"application/json\");\nmyHeaders.append(\"content-type\", \"application/json\");\nmyHeaders.append(\"x-butter-webhook-expiration\", \"1698251668\");\nmyHeaders.append(\"x-butter-webhook-signature\", \"06cf8a25ef73841fea0b20f13499f173e7767da53cccba2412b261816515f30d\");\nmyHeaders.append(\"x-butter-webhook-deduplication-id\", \"47bce5c74f589f4867dbd57e9ca9f808\");\n\nvar raw = JSON.stringify({\n   \"timestamp\": \"2023-10-25T03:57:26Z\",\n   \"invoice_id\": \"inv_12345\",\n   \"recovered\": true\n});\n\nvar requestOptions = {\n   method: 'POST',\n   headers: myHeaders,\n   body: raw,\n   redirect: 'follow'\n};\n\nfetch(\"https://api.<merchant-url>.com/webhooks/end-of-dunning\", requestOptions)\n   .then(response => response.text())\n   .then(result => console.log(result))\n   .catch(error => console.log('error', error));"
        },
        {
          "id": "9se4hDKaVOXCj99M0w-CS",
          "language": "ruby",
          "label": "Ruby",
          "code": "require \"uri\"\nrequire \"json\"\nrequire \"net/http\"\n\nurl = URI(\"https://api.<merchant-url>.com/webhooks/end-of-dunning\")\n\nhttps = Net::HTTP.new(url.host, url.port)\nhttps.use_ssl = true\n\nrequest = Net::HTTP::Post.new(url)\nrequest[\"accept\"] = \"application/json\"\nrequest[\"content-type\"] = \"application/json\"\nrequest[\"x-butter-webhook-expiration\"] = \"1698251668\"\nrequest[\"x-butter-webhook-signature\"] = \"06cf8a25ef73841fea0b20f13499f173e7767da53cccba2412b261816515f30d\"\nrequest[\"x-butter-webhook-deduplication-id\"] = \"47bce5c74f589f4867dbd57e9ca9f808\"\nrequest.body = JSON.dump({\n   \"timestamp\": \"2023-10-25T03:57:26Z\",\n   \"invoice_id\": \"inv_12345\",\n   \"recovered\": true\n})\n\nresponse = https.request(request)\nputs response.read_body\n"
        },
        {
          "id": "CE-Ys_MYunfpgbZFyOqvP",
          "language": "python",
          "label": "Python",
          "code": "import requests\nimport json\n\nurl = \"https://api.<merchant-url>.com/webhooks/end-of-dunning\"\n\npayload = json.dumps({\n   \"timestamp\": \"2023-10-25T03:57:26Z\",\n   \"invoice_id\": \"inv_12345\",\n   \"recovered\": True\n})\nheaders = {\n   'accept': 'application/json',\n   'content-type': 'application/json',\n   'x-butter-webhook-expiration': '1698251668',\n   'x-butter-webhook-signature': '06cf8a25ef73841fea0b20f13499f173e7767da53cccba2412b261816515f30d',\n   'x-butter-webhook-deduplication-id': '47bce5c74f589f4867dbd57e9ca9f808'\n}\n\nresponse = requests.request(\"POST\", url, headers=headers, data=payload)\n\nprint(response.text)\n"
        }
      ],
      "selectedLanguageId": "csCoKHLhicOaz83WR3DM0"
    },
    "results": {
      "languages": [
        {
          "id": "response-0-200",
          "language": "200",
          "code": "// Event Delivered Successfully\n{\n  \"detail\": \"Event delivered successfully\"\n}"
        },
        {
          "id": "response-1-500",
          "language": "500",
          "code": "// Event Not Delivered\n{\n  \"detail\": \"Server is unavailable\"\n}"
        }
      ],
      "selectedLanguageId": "response-0-200"
    }
  },
  "children": [
    {
      "id": "zpw8X70ZYGzjah1TTHieQ",
      "type": "paragraph",
      "children": [
        {
          "text": "Butter will send webhooks via HTTP POST requests to this endpoint when dunning is complete and we will no longer make attempts to recover the payment. Butter guarantees 'at least once' delivery of events, and provides a deduplication ID to enable integrators to handle duplicate event delivery."
        }
      ]
    },
    {
      "id": "xc01nRm0l0ABEA8H_dYCV",
      "type": "paragraph",
      "children": [
        {
          "text": "Note - The Butter webhook payload is a simple JSON object containing the attributes specified below. The payload does not contain a higher-level 'body' object that contains the other attributes."
        }
      ]
    },
    {
      "id": "LbZPQLezFfaKacgKxXiqZ",
      "type": "paragraph",
      "children": [
        {
          "text": "Add field values in the boxes below to create an example payload on the right side of the screen."
        }
      ]
    }
  ]
}