{
  "info": {
    "_postman_id": "c4e8a1b7-2d64-4f0a-9e53-7b18d6a0c2f4",
    "name": "Webhookah",
    "description": "Create a Webhookah receive URL, send a webhook, and read what arrived.\n\nSetup:\n1. Create an API key in Dashboard → API Key (Plus or Enterprise).\n2. Paste it into the collection variable `apiKey`.\n3. Run the folder **Try a webhook** from top to bottom.\n\nInstall guide: https://webhookah.com/docs/integration/postman\nAPI guide: https://webhookah.com/docs/integration/api\nOpenAPI reference: https://webhookah.com/api-reference/",
    "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
  },
  "auth": {
    "type": "bearer",
    "bearer": [
      {
        "key": "token",
        "value": "{{apiKey}}",
        "type": "string"
      }
    ]
  },
  "variable": [
    {
      "key": "baseUrl",
      "value": "https://webhookah.com",
      "type": "string",
      "description": "API origin. Use http://localhost:1323 for a local server."
    },
    {
      "key": "apiKey",
      "value": "",
      "type": "string",
      "description": "API key from Dashboard → API Key. It starts with wh-."
    },
    {
      "key": "endpointToken",
      "value": "",
      "type": "string",
      "description": "Filled by Create an endpoint. Token segment of the public receive URL."
    },
    {
      "key": "endpointUrl",
      "value": "",
      "type": "string",
      "description": "Filled by Create an endpoint. Public URL that receives webhooks."
    }
  ],
  "item": [
    {
      "name": "Try a webhook",
      "description": "Run these five requests in order. Create an endpoint saves `endpointToken` and `endpointUrl`. Clear and delete remove only that endpoint and its captured requests.\n\nClear the requests while the endpoint still exists. Deleting the endpoint also removes any requests still stored for it, and that URL stops accepting traffic.",
      "item": [
        {
          "name": "Create an endpoint",
          "request": {
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/api/endpoints",
            "description": "`POST /api/endpoints` takes no body and returns `201` with `url` and `token`. A `403` body `{\"error\":\"URL limit reached for your plan\"}` means the account is at its endpoint limit."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Status is 201', function () {",
                  "    pm.response.to.have.status(201);",
                  "});",
                  "",
                  "if (pm.response.code !== 201) {",
                  "    return;",
                  "}",
                  "",
                  "const body = pm.response.json();",
                  "pm.test('Response includes url and token', function () {",
                  "    pm.expect(body.token).to.be.a('string').and.not.empty;",
                  "    pm.expect(body.url).to.be.a('string').and.not.empty;",
                  "});",
                  "pm.collectionVariables.set('endpointToken', body.token);",
                  "pm.collectionVariables.set('endpointUrl', body.url);"
                ]
              }
            }
          ]
        },
        {
          "name": "Send a sample webhook",
          "request": {
            "auth": {
              "type": "noauth"
            },
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"event\": \"order.paid\",\n  \"id\": \"ord_123\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": "{{endpointUrl}}",
            "description": "POST the saved public URL. This request sends no API key. Expect `200` and `{\"message\":\"Webhook received successfully\"}`."
          },
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const endpointUrl = pm.collectionVariables.get('endpointUrl');",
                  "if (!endpointUrl) {",
                  "    throw new Error('Run Create an endpoint first so endpointUrl is set.');",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Status is 200', function () {",
                  "    pm.response.to.have.status(200);",
                  "});",
                  "",
                  "const body = pm.response.json();",
                  "pm.test('Webhook was stored', function () {",
                  "    pm.expect(body.message).to.eql('Webhook received successfully');",
                  "    pm.expect(body.id).to.be.a('number');",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "List requests for this endpoint",
          "request": {
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/api/requests/endpoint/{{endpointToken}}",
            "description": "Returns the requests captured by the endpoint from Create an endpoint. `body.json` is the parsed object when the sender used `Content-Type: application/json`. `body.raw` is the original text."
          },
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const token = pm.collectionVariables.get('endpointToken');",
                  "if (!token) {",
                  "    throw new Error('Run Create an endpoint first so endpointToken is set.');",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Status is 200', function () {",
                  "    pm.response.to.have.status(200);",
                  "});",
                  "",
                  "const requests = pm.response.json();",
                  "pm.test('Captured the sample webhook', function () {",
                  "    pm.expect(requests).to.be.an('array');",
                  "    const match = requests.find(function (item) {",
                  "        return item.method === 'POST' && item.body && item.body.raw && item.body.raw.indexOf('order.paid') !== -1;",
                  "    });",
                  "    pm.expect(match, 'POST with order.paid').to.be.an('object');",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Clear requests for this endpoint",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": "{{baseUrl}}/api/requests/endpoint/{{endpointToken}}",
            "description": "Deletes captured requests for this endpoint. Expect `204`. Run this while the endpoint still exists."
          },
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const token = pm.collectionVariables.get('endpointToken');",
                  "if (!token) {",
                  "    throw new Error('Run Create an endpoint first so endpointToken is set.');",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Status is 204', function () {",
                  "    pm.response.to.have.status(204);",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Delete this endpoint",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": "{{baseUrl}}/api/endpoints/{{endpointToken}}",
            "description": "Deletes the endpoint created in this folder. Expect `204`. The public URL stops accepting traffic."
          },
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const token = pm.collectionVariables.get('endpointToken');",
                  "if (!token) {",
                  "    throw new Error('Run Create an endpoint first so endpointToken is set.');",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Status is 204', function () {",
                  "    pm.response.to.have.status(204);",
                  "});"
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "name": "Endpoints",
      "description": "Manage receive URLs on the account. Create an endpoint overwrites `endpointToken` and `endpointUrl`.",
      "item": [
        {
          "name": "List endpoints",
          "request": {
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/api/endpoints",
            "description": "Every endpoint on the account."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Status is 200', function () {",
                  "    pm.response.to.have.status(200);",
                  "});",
                  "pm.test('Response is an array', function () {",
                  "    pm.expect(pm.response.json()).to.be.an('array');",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Create an endpoint",
          "request": {
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/api/endpoints",
            "description": "`POST /api/endpoints` takes no body. Saves `url` and `token` on the collection."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Status is 201', function () {",
                  "    pm.response.to.have.status(201);",
                  "});",
                  "",
                  "if (pm.response.code !== 201) {",
                  "    return;",
                  "}",
                  "",
                  "const body = pm.response.json();",
                  "pm.collectionVariables.set('endpointToken', body.token);",
                  "pm.collectionVariables.set('endpointUrl', body.url);"
                ]
              }
            }
          ]
        },
        {
          "name": "Update endpoint badge",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"badgeName\": \"orders\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": "{{baseUrl}}/api/endpoints/{{endpointToken}}",
            "description": "Sets the badge shown for this endpoint. Uses `endpointToken`."
          },
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const token = pm.collectionVariables.get('endpointToken');",
                  "if (!token) {",
                  "    throw new Error('Run Create an endpoint first so endpointToken is set.');",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Status is 200', function () {",
                  "    pm.response.to.have.status(200);",
                  "});",
                  "pm.test('Badge name is orders', function () {",
                  "    pm.expect(pm.response.json().badgeName).to.eql('orders');",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Delete an endpoint",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": "{{baseUrl}}/api/endpoints/{{endpointToken}}",
            "description": "Deletes the endpoint in `endpointToken`. Expect `204`."
          },
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const token = pm.collectionVariables.get('endpointToken');",
                  "if (!token) {",
                  "    throw new Error('Run Create an endpoint first so endpointToken is set.');",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Status is 204', function () {",
                  "    pm.response.to.have.status(204);",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Regenerate endpoints",
          "request": {
            "method": "POST",
            "header": [],
            "url": "{{baseUrl}}/api/endpoint/regenerate",
            "description": "Deletes every endpoint on the account and creates a new one. Expect `200`. The test script saves the new `url` and `token`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Status is 200', function () {",
                  "    pm.response.to.have.status(200);",
                  "});",
                  "",
                  "if (pm.response.code !== 200) {",
                  "    return;",
                  "}",
                  "",
                  "const body = pm.response.json();",
                  "pm.collectionVariables.set('endpointToken', body.token);",
                  "pm.collectionVariables.set('endpointUrl', body.url);"
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "name": "Captured requests",
      "description": "Read or delete requests stored for the account. Header names that start with X- are dropped before storage.",
      "item": [
        {
          "name": "List all requests",
          "request": {
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/api/requests",
            "description": "Every captured request on the account. An empty account returns `[]`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Status is 200', function () {",
                  "    pm.response.to.have.status(200);",
                  "});",
                  "pm.test('Response is an array', function () {",
                  "    pm.expect(pm.response.json()).to.be.an('array');",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "List requests for an endpoint",
          "request": {
            "method": "GET",
            "header": [],
            "url": "{{baseUrl}}/api/requests/endpoint/{{endpointToken}}",
            "description": "Captured requests for `endpointToken`."
          },
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const token = pm.collectionVariables.get('endpointToken');",
                  "if (!token) {",
                  "    throw new Error('Run Create an endpoint first so endpointToken is set.');",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Status is 200', function () {",
                  "    pm.response.to.have.status(200);",
                  "});",
                  "pm.test('Response is an array', function () {",
                  "    pm.expect(pm.response.json()).to.be.an('array');",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Clear requests for an endpoint",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": "{{baseUrl}}/api/requests/endpoint/{{endpointToken}}",
            "description": "Deletes captured requests for `endpointToken`. Expect `204`. The endpoint must still exist."
          },
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const token = pm.collectionVariables.get('endpointToken');",
                  "if (!token) {",
                  "    throw new Error('Run Create an endpoint first so endpointToken is set.');",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Status is 204', function () {",
                  "    pm.response.to.have.status(204);",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "Clear all requests",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": "{{baseUrl}}/api/requests",
            "description": "Deletes every captured request on the account. Endpoints stay. Expect `204`."
          },
          "event": [
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Status is 204', function () {",
                  "    pm.response.to.have.status(204);",
                  "});"
                ]
              }
            }
          ]
        }
      ]
    },
    {
      "name": "Live updates",
      "description": "Open this request on its own. The response stays open.",
      "item": [
        {
          "name": "Listen for requests",
          "request": {
            "method": "GET",
            "header": [
              {
                "key": "Accept",
                "value": "text/event-stream"
              }
            ],
            "url": "{{baseUrl}}/api/sse",
            "description": "`GET /api/sse` returns `text/event-stream`. Requests already stored are sent first, then each new request, plus a heartbeat about every 30 seconds. Cancel the request when you are done watching.\n\nA captured request is one `data:` line whose JSON has `SSEWebhookRequest`. On this stream, `body` is the raw text. A heartbeat is `data:` JSON with `SSEHeartbeat`."
          }
        }
      ]
    },
    {
      "name": "Receive a webhook",
      "description": "Send traffic to `/wh/{{endpointToken}}`. These requests use no API key. Run Create an endpoint first, or paste a token into `endpointToken`. Header names that start with X- are dropped before storage.",
      "auth": {
        "type": "noauth"
      },
      "item": [
        {
          "name": "POST JSON",
          "request": {
            "method": "POST",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"event\": \"order.paid\",\n  \"id\": \"ord_123\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": "{{baseUrl}}/wh/{{endpointToken}}",
            "description": "JSON object body. The list API returns `body.json` plus `body.raw`."
          },
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const token = pm.collectionVariables.get('endpointToken');",
                  "if (!token) {",
                  "    throw new Error('Run Create an endpoint first so endpointToken is set.');",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Status is 200', function () {",
                  "    pm.response.to.have.status(200);",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "GET with a query string",
          "request": {
            "method": "GET",
            "header": [],
            "url": {
              "raw": "{{baseUrl}}/wh/{{endpointToken}}?event=order.paid",
              "host": [
                "{{baseUrl}}"
              ],
              "path": [
                "wh",
                "{{endpointToken}}"
              ],
              "query": [
                {
                  "key": "event",
                  "value": "order.paid"
                }
              ]
            },
            "description": "Query parameters are stored on the captured request as `queryParams`."
          },
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const token = pm.collectionVariables.get('endpointToken');",
                  "if (!token) {",
                  "    throw new Error('Run Create an endpoint first so endpointToken is set.');",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Status is 200', function () {",
                  "    pm.response.to.have.status(200);",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "PUT JSON",
          "request": {
            "method": "PUT",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"event\": \"order.updated\",\n  \"id\": \"ord_123\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": "{{baseUrl}}/wh/{{endpointToken}}",
            "description": "PUT is captured like any other method."
          },
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const token = pm.collectionVariables.get('endpointToken');",
                  "if (!token) {",
                  "    throw new Error('Run Create an endpoint first so endpointToken is set.');",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Status is 200', function () {",
                  "    pm.response.to.have.status(200);",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "PATCH JSON",
          "request": {
            "method": "PATCH",
            "header": [
              {
                "key": "Content-Type",
                "value": "application/json"
              }
            ],
            "body": {
              "mode": "raw",
              "raw": "{\n  \"event\": \"order.updated\",\n  \"id\": \"ord_123\"\n}",
              "options": {
                "raw": {
                  "language": "json"
                }
              }
            },
            "url": "{{baseUrl}}/wh/{{endpointToken}}",
            "description": "PATCH is captured like any other method."
          },
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const token = pm.collectionVariables.get('endpointToken');",
                  "if (!token) {",
                  "    throw new Error('Run Create an endpoint first so endpointToken is set.');",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Status is 200', function () {",
                  "    pm.response.to.have.status(200);",
                  "});"
                ]
              }
            }
          ]
        },
        {
          "name": "DELETE",
          "request": {
            "method": "DELETE",
            "header": [],
            "url": "{{baseUrl}}/wh/{{endpointToken}}",
            "description": "DELETE to the public URL is captured. It does not delete the endpoint. Use Endpoints → Delete an endpoint for that."
          },
          "event": [
            {
              "listen": "prerequest",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "const token = pm.collectionVariables.get('endpointToken');",
                  "if (!token) {",
                  "    throw new Error('Run Create an endpoint first so endpointToken is set.');",
                  "}"
                ]
              }
            },
            {
              "listen": "test",
              "script": {
                "type": "text/javascript",
                "exec": [
                  "pm.test('Status is 200', function () {",
                  "    pm.response.to.have.status(200);",
                  "});"
                ]
              }
            }
          ]
        }
      ]
    }
  ]
}
