• Hootmix
Guides
  • Introduction
  • Quickstart
  • Authentication
  • Tagging events
  • Rate limits
  • Errors
  • Pagination
Links
  • Create a linkPOST
  • List linksGET
  • Get a linkGET
  • Update a linkPATCH
  • Delete a linkDELETE
  • Get link analyticsGET
Events
  • List eventsGET
  • Get an eventGET
  • Tag an eventPUT
Folders
  • List foldersGET
Tags
  • List tagsGET
Domains
  • List domainsGET
Webhooks
  • Webhooks
  • link.clicked
  • link.created
  • link.updated
  • link.deleted
  • webhook.test
API documentation
Sign in

Introduction

The Hootmix API lets you create, change and measure short links from your own code. It is a JSON REST API, and every request belongs to one workspace.

https://www.hootmix.com/api/v1

The version is part of the path. A version never changes in a breaking way. New behavior ships in a new version, and old versions stay available until they are marked deprecated.

  • Authenticate with a workspace API key in the Authorization header.
  • Every response is JSON, except 204 responses which have no body.
  • Requests and responses use camelCase field names and ISO 8601 dates.

Quickstart

Create a key, then shorten your first link in one request. Replace the key with your own.

curl -X POST "https://www.hootmix.com/api/v1/links" \
  -H "Authorization: Bearer $HOOTMIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"target":{"type":"REDIRECT","url":"https://example.com/hello"},"slug":"hello"}'

The response is the link. Share its url, and read its clicks with Get link analytics.

{
  "id": "6650f1c2a1b2c3d4e5f60718",
  "slug": "hello",
  "url": "https://hoot.mx/hello",
  "status": "active"
}

Authentication

Create a key in your workspace under API keys. Each key belongs to one workspace and only sees that workspace's data. Pick the scopes the key needs and an expiry date.

Sign inSign in to pick a workspace and manage its API keys.

Send the key as a bearer token on every request:

Authorization: Bearer hmx_live_ab12cd34_<secret>

The full key is shown only once, when you create it. Hootmix keeps a fingerprint, not the key, so a lost key cannot be recovered. Create a new one and revoke the old one.

  • links.read: read links.
  • links.write: create and change links.
  • links.delete: disable and delete links.
  • analytics.read: read link statistics.
  • events.read: read click events and tags.
  • events.write: tag click events.
  • folders.read: list folders.
  • domains.read: list domains.

A key can only hold scopes that its creator has in the workspace. Keys can have an expiry date, and revoked or expired keys return 401.

Tagging events

Tags label single clicks, not links. Use them to record what happened after a click, such as converted, vip or refund, and then look at those clicks in your analytics.

  • A visitor clicks a short link. Hootmix records a click event and, if you use webhooks, sends a link.clicked request to your server.
  • The request holds the event id in data.event.id.
  • Your server calls PUT /v1/events/{id}/tags with the tags to set.
  • The tags appear under the click in the link's recent visits, on the Tags page, and in analytics. Filter by tag with the tag parameter.
{
  "id": "9d2f6c1e-...",
  "event": "link.clicked",
  "createdAt": "2026-10-19T08:31:12.000Z",
  "data": {
    "link": { "id": "6650f1c2a1b2c3d4e5f60718", "slug": "spring-sale" },
    "event": { "id": "b1f2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d", "countryCode": "ID", "device": "Mobile" }
  }
}
curl -X PUT "https://www.hootmix.com/api/v1/events/b1f2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d/tags" \
  -H "Authorization: Bearer $HOOTMIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tags":["converted"]}'

You can also tag a click by hand: open a link, go to Recent visits and use the tag button under the click. Create, rename and delete tag names on the Tags page.

Rate limits

Each API key can make a limited number of requests in a time window. The limit is set by the Hootmix administrator and applies to every key equally. Every response tells you where you stand:

  • X-RateLimit-Limit: requests allowed per window.
  • X-RateLimit-Remaining: requests left in this window.
  • X-RateLimit-Reset: seconds until the window resets.

When you run out, the API answers 429 with a Retry-After header holding the seconds to wait. Back off and retry after that time.

Errors

Errors use the HTTP status code and a JSON body with a stable code and a readable message. Match on the code, not on the message.

{
  "error": {
    "code": "not_found",
    "message": "Link not found"
  }
}
  • invalid_request: a field is missing or malformed.
  • folder_ambiguous: you sent both folderId and folderName.
  • invalid_api_key: the key is missing, wrong, expired or revoked.
  • insufficient_scope: the key lacks the scope this endpoint needs.
  • not_found / folder_not_found: the link or folder does not exist in this workspace.
  • folder_name_ambiguous / slug_taken: the request clashes with existing data.
  • unprocessable: a plan limit or rule blocked the request.
  • rate_limited: slow down and retry.

Pagination

List endpoints return the newest items first. Use limit (1 to 100, default 20) and pass the cursor.next from one response as cursor in the next request. When cursor.next is null there are no more items.

{
  "data": [ ... ],
  "cursor": { "next": "6650f1c2a1b2c3d4e5f60718" }
}
POST/v1/links

Create a link

Creates a short link. Leave out the domain to use the first active domain and the slug to get a random one.

Required scopelinks.write

Request body

  • targetobjectRequired

    Where the link goes. Required. Pick a type and fill its properties.

    • typestringRequired

      How visitors are routed. Pick one of the redirect types below.

      One ofREDIRECTDEVICESMARTSPLIT

    When type is REDIRECT. Sends every visitor to one URL.

    • urlstringURLRequired

      The destination. An HTTP or HTTPS URL.

  • statusCodeintegerOptional

    The HTTP status used to redirect. 301 and 308 are permanent, 302 and 307 are temporary.

    One of301302307308Default 302
  • domainIdstringIDOptional

    The domain to create the link on. See List domains. Defaults to the first active domain.

  • slugstringSlugOptional

    The path of the short link. Lowercase letters, numbers and dashes. A random one is generated when left out.

    Max 64
  • folderIdstringIDOptional

    The id of a folder. Send either folderId or folderName, never both.

  • folderobjectOptional
    • namestringRequired
      Max 60

Response

201The response body:

  • idstringID

    Unique id of the link.

  • workspaceIdstringID

    The workspace that owns the link.

  • domainIdstringID

    The domain the link lives on.

  • domainobject

    The domain the link lives on.

    • hostnamestring

      The hostname, for example hoot.mx.

  • userIdstringIDoptional

    The user who created the link, when known.

  • folderIdstringIDoptionalnullable

    The folder the link is in, or null.

  • slugstringSlug

    The path after the domain.

  • statusstring

    active links redirect. disabled links stop redirecting but keep their analytics.

    One ofactivedisabled
  • createdAtstringISO 8601

    When the link was created.

  • modestring

    The redirect type. It is the same as target.type and cannot change.

    One ofREDIRECTDEVICESMARTSPLITROTATOR
  • targetobject

    Where the link sends visitors. It is an object whose type decides the other properties.

    • typestring

      How visitors are routed. Pick one of the redirect types below.

      One ofREDIRECTDEVICESMARTSPLIT

    When type is REDIRECT. Sends every visitor to one URL.

    • urlstringURL

      The destination. An HTTP or HTTPS URL.

  • statusCodeinteger

    The HTTP status used to redirect.

    One of301302307308
  • configurationobject

    Extra settings of the link. You can read them here; they are changed in the dashboard.

    • forwardingobject

      Passing query parameters of the short link on to the destination.

      • isActiveboolean

        Whether forwarding is on.

      • modestring

        ALL forwards every parameter. ALLOWLIST forwards only the keys below.

        One ofALLALLOWLIST
      • keysarray<string>

        The parameters forwarded in ALLOWLIST mode.

      • overwriteboolean

        Whether forwarded parameters replace the ones already on the destination.

    • rulesobject

      Redirect rules.

      • timezonestring
      • listarray<object>
        • idstring

          Unique id of the link.

        • destinationstringURL
    • protectionobject

      Access protection.

      • passwordboolean

        Whether the link asks for a password. The password itself is never returned.

    • cloakingobjectoptional

      Link preview settings, present when configured.

      • isActiveboolean

        Whether the custom preview is on.

  • urlstringURL

    The full short link, ready to share.

Notes

  • Send either folderId or folderName, never both. An unknown folder name returns 404, and a name shared by several folders returns 409.
  • The redirect type and the number of links must fit your plan, otherwise the API returns 422.

Errors

  • 400The request is malformed, or both folderId and folderName were sent.
  • 401The API key is missing, invalid, expired or revoked.
  • 403The API key does not have the scope this endpoint needs.
  • 404The link or folder was not found in this workspace.
  • 409The slug is taken, or a folder name matches more than one folder.
  • 422A plan limit or rule blocked the request.
  • 429The key exceeded its rate limit. Wait for Retry-After seconds.
POST /v1/links
Example
curl -X POST "https://www.hootmix.com/api/v1/links" \
  -H "Authorization: Bearer $HOOTMIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"target":{"type":"REDIRECT","url":"https://example.com/spring-sale"},"slug":"spring-sale","folder":{"name":"Campaigns"}}'
Response

201 Created

{
  "id": "6650f1c2a1b2c3d4e5f60718",
  "workspaceId": "6650f0aaa1b2c3d4e5f60001",
  "domainId": "6650f0bbb1b2c3d4e5f60002",
  "domain": {
    "hostname": "hoot.mx"
  },
  "folderId": "6650f0ccc1b2c3d4e5f60003",
  "slug": "spring-sale",
  "status": "active",
  "createdAt": "2026-10-19T08:30:00.000Z",
  "mode": "REDIRECT",
  "target": {
    "type": "REDIRECT",
    "url": "https://example.com/spring-sale"
  },
  "statusCode": 302,
  "configuration": {
    "forwarding": {
      "isActive": false,
      "mode": "ALL",
      "keys": [],
      "overwrite": false
    },
    "rules": {
      "list": [],
      "timezone": "Asia/Jakarta"
    },
    "protection": {
      "password": false
    }
  },
  "url": "https://hoot.mx/spring-sale"
}
GET/v1/links

List links

Returns your links, newest first, one page at a time.

Required scopelinks.read

Query parameters

  • limitintegerOptional

    How many links to return.

    Default 20Min 1Max 100
  • cursorstringIDOptional

    The cursor.next from the previous page.

  • folderIdstringIDOptional

    Only links in this folder. Send either folderId or folderName, never both.

  • folderNamestringOptional

    Only links in the folder with this name. Send either folderId or folderName, never both.

    Max 60

Response

200The response body:

  • dataarray<object>
    • idstringID

      Unique id of the link.

    • workspaceIdstringID

      The workspace that owns the link.

    • domainIdstringID

      The domain the link lives on.

    • domainobject

      The domain the link lives on.

      • hostnamestring

        The hostname, for example hoot.mx.

    • userIdstringIDoptional

      The user who created the link, when known.

    • folderIdstringIDoptionalnullable

      The folder the link is in, or null.

    • slugstringSlug

      The path after the domain.

    • statusstring

      active links redirect. disabled links stop redirecting but keep their analytics.

      One ofactivedisabled
    • createdAtstringISO 8601

      When the link was created.

    • modestring

      The redirect type. It is the same as target.type and cannot change.

      One ofREDIRECTDEVICESMARTSPLITROTATOR
    • targetobject

      Where the link sends visitors. It is an object whose type decides the other properties.

      • typestring

        How visitors are routed. Pick one of the redirect types below.

        One ofREDIRECTDEVICESMARTSPLIT

      When type is REDIRECT. Sends every visitor to one URL.

      • urlstringURL

        The destination. An HTTP or HTTPS URL.

    • statusCodeinteger

      The HTTP status used to redirect.

      One of301302307308
    • configurationobject

      Extra settings of the link. You can read them here; they are changed in the dashboard.

      • forwardingobject

        Passing query parameters of the short link on to the destination.

        • isActiveboolean

          Whether forwarding is on.

        • modestring

          ALL forwards every parameter. ALLOWLIST forwards only the keys below.

          One ofALLALLOWLIST
        • keysarray<string>

          The parameters forwarded in ALLOWLIST mode.

        • overwriteboolean

          Whether forwarded parameters replace the ones already on the destination.

      • rulesobject

        Redirect rules.

        • timezonestring
        • listarray<object>
          • idstring

            Unique id of the link.

          • destinationstringURL
      • protectionobject

        Access protection.

        • passwordboolean

          Whether the link asks for a password. The password itself is never returned.

      • cloakingobjectoptional

        Link preview settings, present when configured.

        • isActiveboolean

          Whether the custom preview is on.

    • urlstringURL

      The full short link, ready to share.

  • nextCursorstringnullable

Notes

  • Pass cursor.next as cursor to get the next page. It is null on the last page.

Errors

  • 400The request is malformed, or both folderId and folderName were sent.
  • 401The API key is missing, invalid, expired or revoked.
  • 403The API key does not have the scope this endpoint needs.
  • 404The link or folder was not found in this workspace.
  • 409The slug is taken, or a folder name matches more than one folder.
  • 429The key exceeded its rate limit. Wait for Retry-After seconds.
GET /v1/links
Example
curl -X GET "https://www.hootmix.com/api/v1/links?limit=20" \
  -H "Authorization: Bearer $HOOTMIX_API_KEY"
Response

200 OK

{
  "data": [
    {
      "id": "6650f1c2a1b2c3d4e5f60718",
      "workspaceId": "6650f0aaa1b2c3d4e5f60001",
      "domainId": "6650f0bbb1b2c3d4e5f60002",
      "domain": {
        "hostname": "hoot.mx"
      },
      "folderId": "6650f0ccc1b2c3d4e5f60003",
      "slug": "spring-sale",
      "status": "active",
      "createdAt": "2026-10-19T08:30:00.000Z",
      "mode": "REDIRECT",
      "target": {
        "type": "REDIRECT",
        "url": "https://example.com/spring-sale"
      },
      "statusCode": 302,
      "configuration": {
        "forwarding": {
          "isActive": false,
          "mode": "ALL",
          "keys": [],
          "overwrite": false
        },
        "rules": {
          "list": [],
          "timezone": "Asia/Jakarta"
        },
        "protection": {
          "password": false
        }
      },
      "url": "https://hoot.mx/spring-sale"
    }
  ],
  "cursor": {
    "next": null
  }
}
GET/v1/links/{id}

Get a link

Returns one link.

Required scopelinks.read

Path parameters

  • idstringIDRequired

    The id of the link.

Response

200The response body:

  • idstringID

    Unique id of the link.

  • workspaceIdstringID

    The workspace that owns the link.

  • domainIdstringID

    The domain the link lives on.

  • domainobject

    The domain the link lives on.

    • hostnamestring

      The hostname, for example hoot.mx.

  • userIdstringIDoptional

    The user who created the link, when known.

  • folderIdstringIDoptionalnullable

    The folder the link is in, or null.

  • slugstringSlug

    The path after the domain.

  • statusstring

    active links redirect. disabled links stop redirecting but keep their analytics.

    One ofactivedisabled
  • createdAtstringISO 8601

    When the link was created.

  • modestring

    The redirect type. It is the same as target.type and cannot change.

    One ofREDIRECTDEVICESMARTSPLITROTATOR
  • targetobject

    Where the link sends visitors. It is an object whose type decides the other properties.

    • typestring

      How visitors are routed. Pick one of the redirect types below.

      One ofREDIRECTDEVICESMARTSPLIT

    When type is REDIRECT. Sends every visitor to one URL.

    • urlstringURL

      The destination. An HTTP or HTTPS URL.

  • statusCodeinteger

    The HTTP status used to redirect.

    One of301302307308
  • configurationobject

    Extra settings of the link. You can read them here; they are changed in the dashboard.

    • forwardingobject

      Passing query parameters of the short link on to the destination.

      • isActiveboolean

        Whether forwarding is on.

      • modestring

        ALL forwards every parameter. ALLOWLIST forwards only the keys below.

        One ofALLALLOWLIST
      • keysarray<string>

        The parameters forwarded in ALLOWLIST mode.

      • overwriteboolean

        Whether forwarded parameters replace the ones already on the destination.

    • rulesobject

      Redirect rules.

      • timezonestring
      • listarray<object>
        • idstring

          Unique id of the link.

        • destinationstringURL
    • protectionobject

      Access protection.

      • passwordboolean

        Whether the link asks for a password. The password itself is never returned.

    • cloakingobjectoptional

      Link preview settings, present when configured.

      • isActiveboolean

        Whether the custom preview is on.

  • urlstringURL

    The full short link, ready to share.

Errors

  • 401The API key is missing, invalid, expired or revoked.
  • 403The API key does not have the scope this endpoint needs.
  • 404The link or folder was not found in this workspace.
  • 429The key exceeded its rate limit. Wait for Retry-After seconds.
GET /v1/links/{id}
curl -X GET "https://www.hootmix.com/api/v1/links/6650f1c2a1b2c3d4e5f60718" \
  -H "Authorization: Bearer $HOOTMIX_API_KEY"
Response

200 OK

{
  "id": "6650f1c2a1b2c3d4e5f60718",
  "workspaceId": "6650f0aaa1b2c3d4e5f60001",
  "domainId": "6650f0bbb1b2c3d4e5f60002",
  "domain": {
    "hostname": "hoot.mx"
  },
  "folderId": "6650f0ccc1b2c3d4e5f60003",
  "slug": "spring-sale",
  "status": "active",
  "createdAt": "2026-10-19T08:30:00.000Z",
  "mode": "REDIRECT",
  "target": {
    "type": "REDIRECT",
    "url": "https://example.com/spring-sale"
  },
  "statusCode": 302,
  "configuration": {
    "forwarding": {
      "isActive": false,
      "mode": "ALL",
      "keys": [],
      "overwrite": false
    },
    "rules": {
      "list": [],
      "timezone": "Asia/Jakarta"
    },
    "protection": {
      "password": false
    }
  },
  "url": "https://hoot.mx/spring-sale"
}
PATCH/v1/links/{id}

Update a link

Changes the destination or settings of a link. Send only what you want to change.

Required scopelinks.write

Path parameters

  • idstringIDRequired

    The id of the link.

Request body

  • targetobjectOptional

    The new destination. Its type must be the link's current type.

    • typestringRequired

      How visitors are routed. Pick one of the redirect types below.

      One ofREDIRECTDEVICESMARTSPLIT

    When type is REDIRECT. Sends every visitor to one URL.

    • urlstringURLRequired

      The destination. An HTTP or HTTPS URL.

  • statusCodeintegerOptional

    The new redirect status.

    One of301302307308
  • statusstringOptional

    Use disabled to stop the link from redirecting, and active to turn it back on.

    One ofactivedisabled
  • folderIdstringIDOptionalnullable

    Move the link to this folder, or send null to take it out of its folder. Send either folderId or folderName, never both.

  • folderobjectOptional
    • namestringRequired
      Max 60

Response

200The response body:

  • idstringID

    Unique id of the link.

  • workspaceIdstringID

    The workspace that owns the link.

  • domainIdstringID

    The domain the link lives on.

  • domainobject

    The domain the link lives on.

    • hostnamestring

      The hostname, for example hoot.mx.

  • userIdstringIDoptional

    The user who created the link, when known.

  • folderIdstringIDoptionalnullable

    The folder the link is in, or null.

  • slugstringSlug

    The path after the domain.

  • statusstring

    active links redirect. disabled links stop redirecting but keep their analytics.

    One ofactivedisabled
  • createdAtstringISO 8601

    When the link was created.

  • modestring

    The redirect type. It is the same as target.type and cannot change.

    One ofREDIRECTDEVICESMARTSPLITROTATOR
  • targetobject

    Where the link sends visitors. It is an object whose type decides the other properties.

    • typestring

      How visitors are routed. Pick one of the redirect types below.

      One ofREDIRECTDEVICESMARTSPLIT

    When type is REDIRECT. Sends every visitor to one URL.

    • urlstringURL

      The destination. An HTTP or HTTPS URL.

  • statusCodeinteger

    The HTTP status used to redirect.

    One of301302307308
  • configurationobject

    Extra settings of the link. You can read them here; they are changed in the dashboard.

    • forwardingobject

      Passing query parameters of the short link on to the destination.

      • isActiveboolean

        Whether forwarding is on.

      • modestring

        ALL forwards every parameter. ALLOWLIST forwards only the keys below.

        One ofALLALLOWLIST
      • keysarray<string>

        The parameters forwarded in ALLOWLIST mode.

      • overwriteboolean

        Whether forwarded parameters replace the ones already on the destination.

    • rulesobject

      Redirect rules.

      • timezonestring
      • listarray<object>
        • idstring

          Unique id of the link.

        • destinationstringURL
    • protectionobject

      Access protection.

      • passwordboolean

        Whether the link asks for a password. The password itself is never returned.

    • cloakingobjectoptional

      Link preview settings, present when configured.

      • isActiveboolean

        Whether the custom preview is on.

  • urlstringURL

    The full short link, ready to share.

Notes

  • The redirect type of a link cannot change. Create a new link instead.
  • Changing the destination does not change the short link or its analytics.

Errors

  • 400The request is malformed, or both folderId and folderName were sent.
  • 401The API key is missing, invalid, expired or revoked.
  • 403The API key does not have the scope this endpoint needs.
  • 404The link or folder was not found in this workspace.
  • 409The slug is taken, or a folder name matches more than one folder.
  • 422A plan limit or rule blocked the request.
  • 429The key exceeded its rate limit. Wait for Retry-After seconds.
PATCH /v1/links/{id}
Example
curl -X PATCH "https://www.hootmix.com/api/v1/links/6650f1c2a1b2c3d4e5f60718" \
  -H "Authorization: Bearer $HOOTMIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"target":{"type":"REDIRECT","url":"https://example.com/autumn-sale"},"statusCode":301}'
Response

200 OK

{
  "id": "6650f1c2a1b2c3d4e5f60718",
  "workspaceId": "6650f0aaa1b2c3d4e5f60001",
  "domainId": "6650f0bbb1b2c3d4e5f60002",
  "domain": {
    "hostname": "hoot.mx"
  },
  "folderId": "6650f0ccc1b2c3d4e5f60003",
  "slug": "spring-sale",
  "status": "active",
  "createdAt": "2026-10-19T08:30:00.000Z",
  "mode": "REDIRECT",
  "target": {
    "type": "REDIRECT",
    "url": "https://example.com/autumn-sale"
  },
  "statusCode": 301,
  "configuration": {
    "forwarding": {
      "isActive": false,
      "mode": "ALL",
      "keys": [],
      "overwrite": false
    },
    "rules": {
      "list": [],
      "timezone": "Asia/Jakarta"
    },
    "protection": {
      "password": false
    }
  },
  "url": "https://hoot.mx/spring-sale"
}
DELETE/v1/links/{id}

Delete a link

Disables the link, so it stops redirecting but keeps its analytics. Add hard=true to remove it for good.

Required scopelinks.delete

Path parameters

  • idstringIDRequired

    The id of the link.

Query parameters

  • hardbooleanOptional

    Set to true to delete the link together with its analytics. This cannot be undone.

    Default false

Response

204Returns an empty response.

Notes

  • Both forms answer 204 with no body.
  • A disabled link can be turned on again with Update a link.

Errors

  • 401The API key is missing, invalid, expired or revoked.
  • 403The API key does not have the scope this endpoint needs.
  • 404The link or folder was not found in this workspace.
  • 429The key exceeded its rate limit. Wait for Retry-After seconds.
DELETE /v1/links/{id}
Example
curl -X DELETE "https://www.hootmix.com/api/v1/links/6650f1c2a1b2c3d4e5f60718" \
  -H "Authorization: Bearer $HOOTMIX_API_KEY"
Response

204 No Content

No response body
GET/v1/links/{id}/stats

Get link analytics

Returns clicks, visitors, a time series and breakdowns for one link.

Required scopeanalytics.read

Path parameters

  • idstringIDRequired

    The id of the link.

Query parameters

  • daysintegerOptional

    The range to report.

    One of173090Default 7
  • tagstringOptional

    Only count events tagged with this tag.

    Max 60

Response

200The response body:

  • visitsinteger

    Total clicks in the period.

  • visitorsobject

    Visitor counts.

    • uniqueinteger

      Distinct visitors in the period.

  • tagsarray<object>

    Clicks grouped by the tags they carry. A click with two tags counts under both.

    • _idstring

      The value being counted, for example a browser name.

    • countinteger

      Number of clicks.

  • seriesarray<object>

    Clicks over time. One point per hour for a 1 day range, otherwise one per day.

    • timestringISO 8601

      Start of the period.

    • availableboolean

      False for periods before analytics were recorded.

    • visitsinteger

      Clicks in that period.

  • breakdownsobject

    Clicks grouped by common dimensions. Each list is sorted by count.

    • sourcesarray<object>

      Referring site, or Direct / unknown.

      • _idstring

        The value being counted, for example a browser name.

      • countinteger

        Number of clicks.

    • devicesarray<object>

      Desktop, Mobile or Tablet.

      • _idstring

        The value being counted, for example a browser name.

      • countinteger

        Number of clicks.

    • browsersarray<object>

      Browser name.

      • _idstring

        The value being counted, for example a browser name.

      • countinteger

        Number of clicks.

    • outcomesarray<object>

      For smart links, how visitors were sent on.

      • _idstring

        The value being counted, for example a browser name.

      • countinteger

        Number of clicks.

    • attemptsarray<object>

      For smart links, the destinations that were tried.

      • _idstring

        The value being counted, for example a browser name.

      • countinteger

        Number of clicks.

    • openingsarray<object>

      For smart links, whether the app seemed to open.

      • _idstring

        The value being counted, for example a browser name.

      • countinteger

        Number of clicks.

  • detailsobject

    More dimensions, each list limited to the top 10.

    • geoobject

      Where visitors are.

      • countryCodesarray<object>

        Clicks per country code.

        • _idstring

          The value being counted, for example a browser name.

        • countinteger

          Number of clicks.

      • regionsarray<object>

        Clicks per country and region.

        • _idstring

          The value being counted, for example a browser name.

        • countinteger

          Number of clicks.

      • citiesarray<object>

        Clicks per country and city.

        • _idstring

          The value being counted, for example a browser name.

        • countinteger

          Number of clicks.

    • agentobject

      What visitors use.

      • osarray<object>

        Operating system name.

        • _idstring

          The value being counted, for example a browser name.

        • countinteger

          Number of clicks.

    • referrerobject

      Where visitors came from.

      • urlsarray<object>

        Referring pages without query strings.

        • _idstring

          The value being counted, for example a browser name.

        • countinteger

          Number of clicks.

    • utmobject

      Campaign parameters on the short link.

      • sourcearray<object>

        Values of utm_source.

        • _idstring

          The value being counted, for example a browser name.

        • countinteger

          Number of clicks.

      • mediumarray<object>

        Values of utm_medium.

        • _idstring

          The value being counted, for example a browser name.

        • countinteger

          Number of clicks.

      • campaignarray<object>

        Values of utm_campaign.

        • _idstring

          The value being counted, for example a browser name.

        • countinteger

          Number of clicks.

  • cutoffstringISO 8601

    The end of the period, which is when the stats were made.

  • historyobject

    Whether the period is fully recorded.

    • sincestringISO 8601nullable

      When analytics recording began, or null when the whole period is covered.

    • isPartialboolean

      True when the period starts before analytics were recorded.

Notes

  • How many days you can ask for depends on your plan. Longer ranges return 422.
  • Tags belong to click events, so tagging an event later changes these numbers.

Errors

  • 400The request is malformed, or both folderId and folderName were sent.
  • 401The API key is missing, invalid, expired or revoked.
  • 403The API key does not have the scope this endpoint needs.
  • 404The link or folder was not found in this workspace.
  • 422A plan limit or rule blocked the request.
  • 429The key exceeded its rate limit. Wait for Retry-After seconds.
GET /v1/links/{id}/stats
Example
curl -X GET "https://www.hootmix.com/api/v1/links/6650f1c2a1b2c3d4e5f60718/stats?days=7" \
  -H "Authorization: Bearer $HOOTMIX_API_KEY"
Response

200 OK

{
  "visits": 128,
  "visitors": {
    "unique": 97
  },
  "tags": [
    {
      "_id": "launch",
      "count": 128
    }
  ],
  "series": [
    {
      "time": "2026-10-12T08:30:00.000Z",
      "available": true,
      "visits": 14
    },
    {
      "time": "2026-10-13T08:30:00.000Z",
      "available": true,
      "visits": 21
    }
  ],
  "breakdowns": {
    "sources": [
      {
        "_id": "Direct / unknown",
        "count": 80
      }
    ],
    "devices": [
      {
        "_id": "Mobile",
        "count": 90
      }
    ],
    "browsers": [
      {
        "_id": "Chrome",
        "count": 70
      }
    ],
    "outcomes": [],
    "attempts": [],
    "openings": []
  },
  "details": {
    "geo": {
      "countryCodes": [
        {
          "_id": "ID",
          "count": 90
        }
      ],
      "regions": [],
      "cities": []
    },
    "agent": {
      "os": [
        {
          "_id": "Android",
          "count": 60
        }
      ]
    },
    "referrer": {
      "urls": []
    },
    "utm": {
      "source": [
        {
          "_id": "newsletter",
          "count": 40
        }
      ],
      "medium": [],
      "campaign": []
    }
  },
  "cutoff": "2026-10-19T08:30:00.000Z",
  "history": {
    "since": null,
    "isPartial": false
  }
}
GET/v1/events

List events

Returns click events, newest first. Bots are not included.

Required scopeevents.read

Query parameters

  • limitintegerOptional

    How many events to return.

    Default 20Min 1Max 100
  • cursorstringOptional

    The cursor.next from the previous page.

  • linkIdstringIDOptional

    Only events of this link.

  • tagstringOptional

    Only events with this tag.

    Max 60

Response

200The response body:

  • dataarray<object>
    • idstring

      Unique id of the event. Use it to read the event and to tag it.

    • linkIdstringID

      The link that was clicked.

    • createdAtstringISO 8601

      When the click happened.

    • tagsarray<string>

      Tags on this event. Empty until you tag it.

      Max 10
    • devicestringnullable

      Desktop, Mobile or Tablet.

    • browserstringnullable

      Browser name.

    • osstringnullable

      Operating system name.

    • referrerstringURLnullable

      The page the visitor came from, without a query string.

    • geoobject

      Where the visitor is.

      • countryCodestringnullable

        ISO 3166-1 alpha-2 country code.

      • regionstringnullable

        Region.

      • citystringnullable

        City.

    • visitorobject

      The visitor behind the click.

      • idstringnullable

        Anonymous id of the visitor, kept in a first-party cookie.

      • isUniquebooleannullable

        True the first time this visitor clicked this link.

    • utmobject

      Campaign parameters on the short link.

      • sourcestringnullable

        Value of utm_source.

      • mediumstringnullable

        Value of utm_medium.

      • campaignstringnullable

        Value of utm_campaign.

  • nextCursorstringnullable

Notes

  • The id of each event is the id the click webhook sends.
  • Pass cursor.next as cursor to get the next page. It is null on the last page.

Errors

  • 400The request is malformed, or both folderId and folderName were sent.
  • 401The API key is missing, invalid, expired or revoked.
  • 403The API key does not have the scope this endpoint needs.
  • 429The key exceeded its rate limit. Wait for Retry-After seconds.
GET /v1/events
Example
curl -X GET "https://www.hootmix.com/api/v1/events?limit=20" \
  -H "Authorization: Bearer $HOOTMIX_API_KEY"
Response

200 OK

{
  "data": [
    {
      "id": "b1f2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
      "linkId": "6650f1c2a1b2c3d4e5f60718",
      "createdAt": "2026-10-19T08:31:12.000Z",
      "tags": [
        "converted"
      ],
      "device": "Mobile",
      "browser": "Chrome",
      "os": "Android",
      "referrer": "https://newsletter.example.com/issue-42",
      "geo": {
        "countryCode": "ID",
        "region": "JK",
        "city": "Jakarta"
      },
      "visitor": {
        "id": "5a0e9d1c-3b7f-4c52-9a1e-2f6d8b4c7e10",
        "isUnique": true
      },
      "utm": {
        "source": "newsletter",
        "medium": "email",
        "campaign": "spring"
      }
    }
  ],
  "cursor": {
    "next": null
  }
}
GET/v1/events/{id}

Get an event

Returns one click event by the id the click webhook sends.

Required scopeevents.read

Path parameters

  • idstringRequired

    The event id. It is data.event.id in the click webhook.

Response

200The response body:

  • idstring

    Unique id of the event. Use it to read the event and to tag it.

  • linkIdstringID

    The link that was clicked.

  • createdAtstringISO 8601

    When the click happened.

  • tagsarray<string>

    Tags on this event. Empty until you tag it.

    Max 10
  • devicestringnullable

    Desktop, Mobile or Tablet.

  • browserstringnullable

    Browser name.

  • osstringnullable

    Operating system name.

  • referrerstringURLnullable

    The page the visitor came from, without a query string.

  • geoobject

    Where the visitor is.

    • countryCodestringnullable

      ISO 3166-1 alpha-2 country code.

    • regionstringnullable

      Region.

    • citystringnullable

      City.

  • visitorobject

    The visitor behind the click.

    • idstringnullable

      Anonymous id of the visitor, kept in a first-party cookie.

    • isUniquebooleannullable

      True the first time this visitor clicked this link.

  • utmobject

    Campaign parameters on the short link.

    • sourcestringnullable

      Value of utm_source.

    • mediumstringnullable

      Value of utm_medium.

    • campaignstringnullable

      Value of utm_campaign.

Errors

  • 401The API key is missing, invalid, expired or revoked.
  • 403The API key does not have the scope this endpoint needs.
  • 404The link or folder was not found in this workspace.
  • 429The key exceeded its rate limit. Wait for Retry-After seconds.
GET /v1/events/{id}
curl -X GET "https://www.hootmix.com/api/v1/events/b1f2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d" \
  -H "Authorization: Bearer $HOOTMIX_API_KEY"
Response

200 OK

{
  "id": "b1f2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
  "linkId": "6650f1c2a1b2c3d4e5f60718",
  "createdAt": "2026-10-19T08:31:12.000Z",
  "tags": [
    "converted"
  ],
  "device": "Mobile",
  "browser": "Chrome",
  "os": "Android",
  "referrer": "https://newsletter.example.com/issue-42",
  "geo": {
    "countryCode": "ID",
    "region": "JK",
    "city": "Jakarta"
  },
  "visitor": {
    "id": "5a0e9d1c-3b7f-4c52-9a1e-2f6d8b4c7e10",
    "isUnique": true
  },
  "utm": {
    "source": "newsletter",
    "medium": "email",
    "campaign": "spring"
  }
}
PUT/v1/events/{id}/tags

Tag an event

Sets the tags of one click event. This replaces the tags it has now.

Required scopeevents.write

Path parameters

  • idstringRequired

    The event id. It is data.event.id in the click webhook.

Request body

  • tagsarray<string>Required

    The tags for this event. Send an empty list to remove them all. New tags are created for you.

    Max 10

Response

200The response body:

  • idstring

    Unique id of the event. Use it to read the event and to tag it.

  • linkIdstringID

    The link that was clicked.

  • createdAtstringISO 8601

    When the click happened.

  • tagsarray<string>

    Tags on this event. Empty until you tag it.

    Max 10
  • devicestringnullable

    Desktop, Mobile or Tablet.

  • browserstringnullable

    Browser name.

  • osstringnullable

    Operating system name.

  • referrerstringURLnullable

    The page the visitor came from, without a query string.

  • geoobject

    Where the visitor is.

    • countryCodestringnullable

      ISO 3166-1 alpha-2 country code.

    • regionstringnullable

      Region.

    • citystringnullable

      City.

  • visitorobject

    The visitor behind the click.

    • idstringnullable

      Anonymous id of the visitor, kept in a first-party cookie.

    • isUniquebooleannullable

      True the first time this visitor clicked this link.

  • utmobject

    Campaign parameters on the short link.

    • sourcestringnullable

      Value of utm_source.

    • mediumstringnullable

      Value of utm_medium.

    • campaignstringnullable

      Value of utm_campaign.

Notes

  • Call this from the server that receives your click webhook, with data.event.id as the id.
  • Tags are lowercase and cannot contain commas. An event can have up to 10 tags, and a workspace up to 50 different tags.
  • Tagged events show up in analytics. Use the tag parameter of Get link analytics or List events to filter by tag.

Errors

  • 400The request is malformed, or both folderId and folderName were sent.
  • 401The API key is missing, invalid, expired or revoked.
  • 403The API key does not have the scope this endpoint needs.
  • 404The link or folder was not found in this workspace.
  • 422A plan limit or rule blocked the request.
  • 429The key exceeded its rate limit. Wait for Retry-After seconds.
PUT /v1/events/{id}/tags
Example
curl -X PUT "https://www.hootmix.com/api/v1/events/b1f2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d/tags" \
  -H "Authorization: Bearer $HOOTMIX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"tags":["converted"]}'
Response

200 OK

{
  "id": "b1f2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
  "linkId": "6650f1c2a1b2c3d4e5f60718",
  "createdAt": "2026-10-19T08:31:12.000Z",
  "tags": [
    "converted"
  ],
  "device": "Mobile",
  "browser": "Chrome",
  "os": "Android",
  "referrer": "https://newsletter.example.com/issue-42",
  "geo": {
    "countryCode": "ID",
    "region": "JK",
    "city": "Jakarta"
  },
  "visitor": {
    "id": "5a0e9d1c-3b7f-4c52-9a1e-2f6d8b4c7e10",
    "isUnique": true
  },
  "utm": {
    "source": "newsletter",
    "medium": "email",
    "campaign": "spring"
  }
}
GET/v1/folders

List folders

Returns every folder in the workspace. Use parentId to rebuild the tree.

Required scopefolders.read

Response

200The response body:

  • dataarray<object>
    • idstringID

      Unique id of the folder.

    • parentIdstringIDnullable

      The parent folder, or null at the top level.

    • namestring

      The folder name.

Errors

  • 401The API key is missing, invalid, expired or revoked.
  • 403The API key does not have the scope this endpoint needs.
  • 429The key exceeded its rate limit. Wait for Retry-After seconds.
GET /v1/folders
curl -X GET "https://www.hootmix.com/api/v1/folders" \
  -H "Authorization: Bearer $HOOTMIX_API_KEY"
Response

200 OK

{
  "data": [
    {
      "id": "6650f0ccc1b2c3d4e5f60003",
      "parentId": null,
      "name": "Campaigns"
    },
    {
      "id": "6650f0ddd1b2c3d4e5f60004",
      "parentId": "6650f0ccc1b2c3d4e5f60003",
      "name": "Autumn"
    }
  ]
}
GET/v1/tags

List tags

Returns the tags of the workspace. A tag exists once you tag an event with it, or create it on the Tags page.

Required scopelinks.read

Response

200The response body:

  • dataarray<object>
    • namestring

      The tag name. Lowercase.

    • createdAtstringISO 8601

      When the tag was created.

Errors

  • 401The API key is missing, invalid, expired or revoked.
  • 403The API key does not have the scope this endpoint needs.
  • 429The key exceeded its rate limit. Wait for Retry-After seconds.
GET /v1/tags
curl -X GET "https://www.hootmix.com/api/v1/tags" \
  -H "Authorization: Bearer $HOOTMIX_API_KEY"
Response

200 OK

{
  "data": [
    {
      "name": "launch",
      "createdAt": "2026-10-01T10:00:00.000Z"
    },
    {
      "name": "newsletter",
      "createdAt": "2026-10-02T09:15:00.000Z"
    }
  ]
}
GET/v1/domains

List domains

Returns the domains you can create links on.

Required scopedomains.read

Response

200The response body:

  • dataarray<object>
    • idstringID

      Unique id of the domain.

    • hostnamestring

      The hostname.

    • kindstring

      GLOBAL domains are shared and managed by Hootmix. WORKSPACE is reserved for domains owned by a workspace.

      One ofGLOBALWORKSPACE

Notes

  • Domains are managed by the Hootmix administrator for now. Adding your own domain through the API is planned.

Errors

  • 401The API key is missing, invalid, expired or revoked.
  • 403The API key does not have the scope this endpoint needs.
  • 429The key exceeded its rate limit. Wait for Retry-After seconds.
GET /v1/domains
curl -X GET "https://www.hootmix.com/api/v1/domains" \
  -H "Authorization: Bearer $HOOTMIX_API_KEY"
Response

200 OK

{
  "data": [
    {
      "id": "6650f0bbb1b2c3d4e5f60002",
      "hostname": "hoot.mx",
      "kind": "GLOBAL"
    }
  ]
}

Webhooks

Webhooks send an HTTP POST to your server when something happens in your workspace, so you do not have to poll the API. Add an endpoint under Webhooks in your workspace, pick the events you want, and optionally limit it to some folders. A folder filter also matches the subfolders.

Every request has a JSON body with the same envelope. What is inside data depends on the event.

{
  "id": "9d2f6c1e-7a4b-4e0d-8f53-2c1b6a9e4d70",
  "event": "link.clicked",
  "createdAt": "2026-10-19T08:31:12.000Z",
  "data": { }
}

Each request also carries these headers:

  • X-Hootmix-Event: the event name.
  • X-Hootmix-Delivery: the id of this delivery attempt.
  • X-Hootmix-Signature: sha256= followed by the HMAC SHA-256 of the raw request body, made with your signing secret.
  • User-Agent: Hootmix-Webhook/1.0

Check the signature before you trust a request. The signing secret is shown once, when you create the endpoint or rotate its secret. Compute the HMAC over the raw body, not over parsed JSON.

Node.js
import { createHmac, timingSafeEqual } from "node:crypto";

const verify = (rawBody, header, secret) => {
  const expected = "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
  return header.length === expected.length && timingSafeEqual(Buffer.from(header), Buffer.from(expected));
};
Python
import hashlib
import hmac

def verify(raw_body: bytes, header: str, secret: str) -> bool:
    expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
    return hmac.compare_digest(header, expected)
PHP
function verify(string $rawBody, string $header, string $secret): bool {
    $expected = "sha256=" . hash_hmac("sha256", $rawBody, $secret);
    return hash_equals($expected, $header);
}

Delivery:

  • Answer with any 2xx status within 5 seconds. Any other status, or no answer, counts as a failure.
  • A failed delivery is retried after 30 seconds, 2 minutes, 8 minutes and 32 minutes. After the fifth failed attempt it is marked failed. The recent deliveries of each endpoint are listed in your workspace.
  • The id in the body stays the same across retries and across your endpoints, so use it to ignore duplicates.
  • Redirects are not followed, and the endpoint must be a public HTTP or HTTPS address.
  • Use Send test on an endpoint to send a webhook.test notification.
Webhook

link.clicked

A visitor clicked a short link. Clicks from bots are not sent.

Payload

  • idstring

    Unique id of this notification. It stays the same across retries and across your endpoints, so use it to ignore duplicates.

  • eventstring

    The click.

    One oflink.clicked
  • createdAtstringISO 8601

    When the notification was created.

  • dataobject

    What happened. The properties depend on the event.

    • linkobject

      The link that was clicked.

      • idstringID

        The link id.

      • urlstringURL

        The short link.

      • slugstring

        The path after the domain.

    • eventobject

      The click.

      • idstring

        The id of the click event. Use it with the API to read or tag the event.

      • visitorIdstring

        Anonymous id of the visitor, kept in a first-party cookie.

      • isUniqueboolean

        True the first time this visitor clicked this link.

      • countryCodestringnullable

        ISO 3166-1 alpha-2 country code, when known.

      • regionstringnullable

        Region, when known.

      • citystringnullable

        City, when known.

      • devicestringnullable

        Desktop, Mobile or Tablet.

      • browserstringnullable

        Browser name.

      • osstringnullable

        Operating system name.

      • referrerstringURLnullable

        The page the visitor came from, without a query string.

      • utmobject

        Campaign parameters on the short link.

        • sourcestringnullable

          Value of utm_source.

        • mediumstringnullable

          Value of utm_medium.

        • campaignstringnullable

          Value of utm_campaign.

Notes

  • data.event.id is the id of the click event. Use it with PUT /v1/events/{id}/tags to tag the click, or GET /v1/events/{id} to read it.
  • Create the endpoint with a folder filter to receive clicks of links in those folders only.
POST your endpoint
{
  "id": "9d2f6c1e-7a4b-4e0d-8f53-2c1b6a9e4d70",
  "event": "link.clicked",
  "createdAt": "2026-10-19T08:31:12.000Z",
  "data": {
    "link": {
      "id": "6650f1c2a1b2c3d4e5f60718",
      "url": "https://hoot.mx/spring-sale",
      "slug": "spring-sale"
    },
    "event": {
      "id": "b1f2c3d4-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
      "visitorId": "5a0e9d1c-3b7f-4c52-9a1e-2f6d8b4c7e10",
      "isUnique": true,
      "countryCode": "ID",
      "region": "JK",
      "city": "Jakarta",
      "device": "Mobile",
      "browser": "Chrome",
      "os": "Android",
      "referrer": "https://newsletter.example.com/issue-42",
      "utm": {
        "source": "newsletter",
        "medium": "email",
        "campaign": "spring"
      }
    }
  }
}
Webhook

link.created

A link was created, from the dashboard, an import or the API.

Payload

  • idstring

    Unique id of this notification. It stays the same across retries and across your endpoints, so use it to ignore duplicates.

  • eventstring

    The event name. It is also in the X-Hootmix-Event header.

    One oflink.created
  • createdAtstringISO 8601

    When the notification was created.

  • dataobject

    What happened. The properties depend on the event.

    • linkobject

      The link this is about.

      • idstringID

        The link id.

      • urlstringURL

        The short link.

      • slugstring

        The path after the domain.

      • modestring

        The redirect type.

        One ofREDIRECTDEVICESMARTSPLIT
      • statusstring

        active or disabled.

        One ofactivedisabled
      • folderIdstringIDnullable

        The folder the link is in, or null.

Notes

  • Importing links sends one notification per link.
POST your endpoint
{
  "id": "9d2f6c1e-7a4b-4e0d-8f53-2c1b6a9e4d70",
  "event": "link.created",
  "createdAt": "2026-10-19T08:31:12.000Z",
  "data": {
    "link": {
      "id": "6650f1c2a1b2c3d4e5f60718",
      "url": "https://hoot.mx/spring-sale",
      "slug": "spring-sale",
      "mode": "REDIRECT",
      "status": "active",
      "folderId": "6650f0ccc1b2c3d4e5f60003"
    }
  }
}
Webhook

link.updated

A link changed: its destination, slug, status or folder.

Payload

  • idstring

    Unique id of this notification. It stays the same across retries and across your endpoints, so use it to ignore duplicates.

  • eventstring

    The event name. It is also in the X-Hootmix-Event header.

    One oflink.updated
  • createdAtstringISO 8601

    When the notification was created.

  • dataobject

    What happened. The properties depend on the event.

    • linkobject

      The link this is about.

      • idstringID

        The link id.

      • urlstringURL

        The short link.

      • slugstring

        The path after the domain.

      • modestring

        The redirect type.

        One ofREDIRECTDEVICESMARTSPLIT
      • statusstring

        active or disabled.

        One ofactivedisabled
      • folderIdstringIDnullable

        The folder the link is in, or null.

POST your endpoint
{
  "id": "9d2f6c1e-7a4b-4e0d-8f53-2c1b6a9e4d70",
  "event": "link.updated",
  "createdAt": "2026-10-19T08:31:12.000Z",
  "data": {
    "link": {
      "id": "6650f1c2a1b2c3d4e5f60718",
      "url": "https://hoot.mx/spring-sale",
      "slug": "spring-sale",
      "mode": "REDIRECT",
      "status": "disabled",
      "folderId": "6650f0ccc1b2c3d4e5f60003"
    }
  }
}
Webhook

link.deleted

A link was deleted. Only its id and slug are sent, because the link no longer exists.

Payload

  • idstring

    Unique id of this notification. It stays the same across retries and across your endpoints, so use it to ignore duplicates.

  • eventstring

    The event name. It is also in the X-Hootmix-Event header.

    One oflink.deleted
  • createdAtstringISO 8601

    When the notification was created.

  • dataobject

    What happened. The properties depend on the event.

    • linkobject

      The link this is about.

      • idstringID

        The link id.

      • slugstring

        The path after the domain.

POST your endpoint
{
  "id": "9d2f6c1e-7a4b-4e0d-8f53-2c1b6a9e4d70",
  "event": "link.deleted",
  "createdAt": "2026-10-19T08:31:12.000Z",
  "data": {
    "link": {
      "id": "6650f1c2a1b2c3d4e5f60718",
      "slug": "spring-sale"
    }
  }
}
Webhook

webhook.test

Sent when you press Send test on an endpoint, so you can check that it works.

Payload

  • idstring

    Unique id of this notification. It stays the same across retries and across your endpoints, so use it to ignore duplicates.

  • eventstring

    The event name. It is also in the X-Hootmix-Event header.

    One ofwebhook.test
  • createdAtstringISO 8601

    When the notification was created.

  • dataobject

    Empty for a test notification.

    POST your endpoint
    {
      "id": "9d2f6c1e-7a4b-4e0d-8f53-2c1b6a9e4d70",
      "event": "webhook.test",
      "createdAt": "2026-10-19T08:31:12.000Z",
      "data": {}
    }