# generated: '2026-08-13' # method: derived # source: openapi/_original/loops-openapi.yaml (webhooks block, info.version 1.21.6) + https://loops.so/docs/webhooks asyncapi: 3.0.0 info: title: Loops Webhooks version: 1.21.6 description: Event catalog for the Loops webhook surface, derived operation-for-operation from the `webhooks` block of the Loops OpenAPI 3.1 document (info.version 1.21.6, published at https://app.loops.so/openapi.json). Loops does not publish an AsyncAPI document; it publishes these events inside its OpenAPI, and this file is a faithful projection of that block into AsyncAPI 3.0.0 so the event surface is readable by event tooling. Every channel, message name, summary and payload schema below comes verbatim from the provider. Delivery is a single HTTP POST to one subscriber-configured endpoint per Loops account, signed with HMAC-SHA256 over `{webhook-id}.{webhook-timestamp}.{raw-body}` and capped at 10 events per second. externalDocs: description: Loops webhooks documentation url: https://loops.so/docs/webhooks defaultContentType: application/json servers: subscriber: host: '{subscriberEndpoint}' protocol: https description: The single endpoint URL configured by the Loops account holder at https://app.loops.so/settings?page=webhooks. Loops permits one webhook endpoint per account. variables: subscriberEndpoint: description: Your HTTPS endpoint that receives Loops events. default: example.com/webhooks/loops channels: contactUnsubscribed: address: / title: Contact unsubscribed description: Delivered by HTTP POST to the account webhook endpoint. servers: - $ref: '#/servers/subscriber' messages: contactUnsubscribed: $ref: '#/components/messages/contactUnsubscribed' contactCreated: address: / title: Contact created description: Delivered by HTTP POST to the account webhook endpoint. servers: - $ref: '#/servers/subscriber' messages: contactCreated: $ref: '#/components/messages/contactCreated' contactDeleted: address: / title: Contact deleted description: Delivered by HTTP POST to the account webhook endpoint. servers: - $ref: '#/servers/subscriber' messages: contactDeleted: $ref: '#/components/messages/contactDeleted' contactMailingListSubscribed: address: / title: Contact subscribed to mailing list description: Delivered by HTTP POST to the account webhook endpoint. servers: - $ref: '#/servers/subscriber' messages: contactMailingListSubscribed: $ref: '#/components/messages/contactMailingListSubscribed' contactMailingListUnsubscribed: address: / title: Contact unsubscribed from mailing list description: Delivered by HTTP POST to the account webhook endpoint. servers: - $ref: '#/servers/subscriber' messages: contactMailingListUnsubscribed: $ref: '#/components/messages/contactMailingListUnsubscribed' transactionalEmailSent: address: / title: Transactional email sent description: Delivered by HTTP POST to the account webhook endpoint. servers: - $ref: '#/servers/subscriber' messages: transactionalEmailSent: $ref: '#/components/messages/transactionalEmailSent' campaignEmailSent: address: / title: Campaign email sent description: Delivered by HTTP POST to the account webhook endpoint. servers: - $ref: '#/servers/subscriber' messages: campaignEmailSent: $ref: '#/components/messages/campaignEmailSent' loopEmailSent: address: / title: Loop email sent description: Delivered by HTTP POST to the account webhook endpoint. servers: - $ref: '#/servers/subscriber' messages: loopEmailSent: $ref: '#/components/messages/loopEmailSent' emailDelivered: address: / title: Email delivered description: Delivered by HTTP POST to the account webhook endpoint. servers: - $ref: '#/servers/subscriber' messages: emailDelivered: $ref: '#/components/messages/emailDelivered' emailSoftBounced: address: / title: Email soft bounced description: Delivered by HTTP POST to the account webhook endpoint. servers: - $ref: '#/servers/subscriber' messages: emailSoftBounced: $ref: '#/components/messages/emailSoftBounced' emailHardBounced: address: / title: Email hard bounced description: Delivered by HTTP POST to the account webhook endpoint. servers: - $ref: '#/servers/subscriber' messages: emailHardBounced: $ref: '#/components/messages/emailHardBounced' emailClicked: address: / title: Email clicked description: Delivered by HTTP POST to the account webhook endpoint. servers: - $ref: '#/servers/subscriber' messages: emailClicked: $ref: '#/components/messages/emailClicked' emailUnsubscribed: address: / title: Email unsubscribed description: Delivered by HTTP POST to the account webhook endpoint. servers: - $ref: '#/servers/subscriber' messages: emailUnsubscribed: $ref: '#/components/messages/emailUnsubscribed' emailResubscribed: address: / title: Email resubscribed description: Delivered by HTTP POST to the account webhook endpoint. servers: - $ref: '#/servers/subscriber' messages: emailResubscribed: $ref: '#/components/messages/emailResubscribed' emailOpened: address: / title: Email opened description: Delivered by HTTP POST to the account webhook endpoint. servers: - $ref: '#/servers/subscriber' messages: emailOpened: $ref: '#/components/messages/emailOpened' emailSpamReported: address: / title: Email spam reported description: Delivered by HTTP POST to the account webhook endpoint. servers: - $ref: '#/servers/subscriber' messages: emailSpamReported: $ref: '#/components/messages/emailSpamReported' testingTestEvent: address: / title: Test event description: Delivered by HTTP POST to the account webhook endpoint. servers: - $ref: '#/servers/subscriber' messages: testingTestEvent: $ref: '#/components/messages/testingTestEvent' operations: receive_contactUnsubscribed: action: receive channel: $ref: '#/channels/contactUnsubscribed' summary: Contact unsubscribed messages: - $ref: '#/channels/contactUnsubscribed/messages/contactUnsubscribed' receive_contactCreated: action: receive channel: $ref: '#/channels/contactCreated' summary: Contact created messages: - $ref: '#/channels/contactCreated/messages/contactCreated' receive_contactDeleted: action: receive channel: $ref: '#/channels/contactDeleted' summary: Contact deleted messages: - $ref: '#/channels/contactDeleted/messages/contactDeleted' receive_contactMailingListSubscribed: action: receive channel: $ref: '#/channels/contactMailingListSubscribed' summary: Contact subscribed to mailing list messages: - $ref: '#/channels/contactMailingListSubscribed/messages/contactMailingListSubscribed' receive_contactMailingListUnsubscribed: action: receive channel: $ref: '#/channels/contactMailingListUnsubscribed' summary: Contact unsubscribed from mailing list messages: - $ref: '#/channels/contactMailingListUnsubscribed/messages/contactMailingListUnsubscribed' receive_transactionalEmailSent: action: receive channel: $ref: '#/channels/transactionalEmailSent' summary: Transactional email sent messages: - $ref: '#/channels/transactionalEmailSent/messages/transactionalEmailSent' receive_campaignEmailSent: action: receive channel: $ref: '#/channels/campaignEmailSent' summary: Campaign email sent messages: - $ref: '#/channels/campaignEmailSent/messages/campaignEmailSent' receive_loopEmailSent: action: receive channel: $ref: '#/channels/loopEmailSent' summary: Loop email sent messages: - $ref: '#/channels/loopEmailSent/messages/loopEmailSent' receive_emailDelivered: action: receive channel: $ref: '#/channels/emailDelivered' summary: Email delivered messages: - $ref: '#/channels/emailDelivered/messages/emailDelivered' receive_emailSoftBounced: action: receive channel: $ref: '#/channels/emailSoftBounced' summary: Email soft bounced messages: - $ref: '#/channels/emailSoftBounced/messages/emailSoftBounced' receive_emailHardBounced: action: receive channel: $ref: '#/channels/emailHardBounced' summary: Email hard bounced messages: - $ref: '#/channels/emailHardBounced/messages/emailHardBounced' receive_emailClicked: action: receive channel: $ref: '#/channels/emailClicked' summary: Email clicked messages: - $ref: '#/channels/emailClicked/messages/emailClicked' receive_emailUnsubscribed: action: receive channel: $ref: '#/channels/emailUnsubscribed' summary: Email unsubscribed messages: - $ref: '#/channels/emailUnsubscribed/messages/emailUnsubscribed' receive_emailResubscribed: action: receive channel: $ref: '#/channels/emailResubscribed' summary: Email resubscribed messages: - $ref: '#/channels/emailResubscribed/messages/emailResubscribed' receive_emailOpened: action: receive channel: $ref: '#/channels/emailOpened' summary: Email opened messages: - $ref: '#/channels/emailOpened/messages/emailOpened' receive_emailSpamReported: action: receive channel: $ref: '#/channels/emailSpamReported' summary: Email spam reported messages: - $ref: '#/channels/emailSpamReported/messages/emailSpamReported' receive_testingTestEvent: action: receive channel: $ref: '#/channels/testingTestEvent' summary: Test event messages: - $ref: '#/channels/testingTestEvent/messages/testingTestEvent' components: messages: contactUnsubscribed: name: contactUnsubscribed title: Contact unsubscribed summary: Sent when a contact is unsubscribed from your audience, or when a contact is deleted from your audience (alongside `contact.deleted`). This is not the same as a contact unsubscribing from a mailing list (see `contact.mailingList.unsubscribed`). Contains a `contactIdentity` object. contentType: application/json payload: $ref: '#/components/schemas/WebhookContactUnsubscribedPayload' headers: $ref: '#/components/schemas/LoopsWebhookHeaders' contactCreated: name: contactCreated title: Contact created summary: Sent when a new contact is created in your audience. Contains a `contactIdentity` object and a full `contact` object with all of the new contact's properties (including custom properties). When double opt-in is enabled, contact webhooks do not fire until the contact is confirmed; `contact.created` only fires for form-created contacts once they confirm their subscription. `optInStatus` will never be `"pending"` or `"rejected"` for this event. contentType: application/json payload: $ref: '#/components/schemas/WebhookContactCreatedPayload' headers: $ref: '#/components/schemas/LoopsWebhookHeaders' contactDeleted: name: contactDeleted title: Contact deleted summary: Sent when a contact is deleted from your audience. Contains a `contactIdentity` object. contentType: application/json payload: $ref: '#/components/schemas/WebhookContactDeletedPayload' headers: $ref: '#/components/schemas/LoopsWebhookHeaders' contactMailingListSubscribed: name: contactMailingListSubscribed title: Contact subscribed to mailing list summary: Sent when a contact is subscribed to a mailing list. Contains `contactIdentity` and `mailingList` objects. When double opt-in is enabled, this event only fires after the contact confirms their subscription. contentType: application/json payload: $ref: '#/components/schemas/WebhookContactMailingListSubscribedPayload' headers: $ref: '#/components/schemas/LoopsWebhookHeaders' contactMailingListUnsubscribed: name: contactMailingListUnsubscribed title: Contact unsubscribed from mailing list summary: Sent when a contact is unsubscribed from a mailing list. This is not the same as a contact unsubscribing from your audience (see `contact.unsubscribed`). Contains `contactIdentity` and `mailingList` objects. contentType: application/json payload: $ref: '#/components/schemas/WebhookContactMailingListUnsubscribedPayload' headers: $ref: '#/components/schemas/LoopsWebhookHeaders' transactionalEmailSent: name: transactionalEmailSent title: Transactional email sent summary: Sent when a transactional email is sent to a contact. Contains a `transactionalId` value plus `contactIdentity` and `email` objects. contentType: application/json payload: $ref: '#/components/schemas/WebhookTransactionalEmailSentPayload' headers: $ref: '#/components/schemas/LoopsWebhookHeaders' campaignEmailSent: name: campaignEmailSent title: Campaign email sent summary: Sent when a campaign email is sent to a contact. This event fires for every campaign send (for example, sending to 1,000 contacts produces 1,000 events). Contains `campaignId` and `campaignName` plus `contactIdentity` and `email` objects. If the campaign was sent to one or more mailing lists, a `mailingLists` array is also included. contentType: application/json payload: $ref: '#/components/schemas/WebhookCampaignEmailSentPayload' headers: $ref: '#/components/schemas/LoopsWebhookHeaders' loopEmailSent: name: loopEmailSent title: Loop email sent summary: Sent when a workflow email is sent to a contact. This event keeps its `loop` naming for compatibility (Loops was renamed to Workflows on May 6, 2026). This event fires for every contact in a workflow (for example, 1,000 recipients produces 1,000 events). Contains `loopId` and `loopName` plus `contactIdentity` and `email` objects. If the workflow was sent to one or more mailing lists, a `mailingLists` array is also included. contentType: application/json payload: $ref: '#/components/schemas/WebhookLoopEmailSentPayload' headers: $ref: '#/components/schemas/LoopsWebhookHeaders' emailDelivered: name: emailDelivered title: Email delivered summary: Sent when an email is delivered to its recipient. Contains `sourceType` (`campaign`, `loop`, or `transactional`) and the related `campaignId`, `loopId` (workflow ID), or `transactionalId`, plus `contactIdentity` and `email` objects. contentType: application/json payload: $ref: '#/components/schemas/WebhookEmailDeliveredPayload' headers: $ref: '#/components/schemas/LoopsWebhookHeaders' emailSoftBounced: name: emailSoftBounced title: Email soft bounced summary: Sent when an email soft bounces. Soft bounces are temporary delivery failures (for example, a connection timing out). Soft bounces are retried multiple times and the email may still be delivered. Contains `sourceType` and the related `campaignId`, `loopId` (workflow ID), or `transactionalId`, plus `contactIdentity` and `email` objects. contentType: application/json payload: $ref: '#/components/schemas/WebhookEmailSoftBouncedPayload' headers: $ref: '#/components/schemas/LoopsWebhookHeaders' emailHardBounced: name: emailHardBounced title: Email hard bounced summary: Sent when an email hard bounces. Hard bounces are persistent delivery failures (for example, a mailbox that does not exist); the email will not be delivered. In Loops, a hard bounce also unsubscribes the contact from your audience, so a `contact.unsubscribed` event will also be sent. Contains `sourceType` and the related `campaignId`, `loopId` (workflow ID), or `transactionalId`, plus `contactIdentity` and `email` objects. contentType: application/json payload: $ref: '#/components/schemas/WebhookEmailHardBouncedPayload' headers: $ref: '#/components/schemas/LoopsWebhookHeaders' emailClicked: name: emailClicked title: Email clicked summary: Sent when a link in a campaign or workflow email is clicked. Not available for transactional emails because link clicks are not tracked for transactional emails. Contains `sourceType` and the related `campaignId` or `loopId` (workflow ID), plus `contactIdentity` and `email` objects. contentType: application/json payload: $ref: '#/components/schemas/WebhookEmailClickedPayload' headers: $ref: '#/components/schemas/LoopsWebhookHeaders' emailUnsubscribed: name: emailUnsubscribed title: Email unsubscribed summary: Sent when a recipient unsubscribes from marketing email or a mailing list using an email's "Unsubscribe" link. A `contact.unsubscribed` or `contact.mailingList.unsubscribed` event will also be sent depending on whether the email was sent to a mailing list. Not available for transactional emails because unsubscribe links are not included for transactional emails. Contains `sourceType` and the related `campaignId` or `loopId` (workflow ID), plus `contactIdentity` and `email` objects. contentType: application/json payload: $ref: '#/components/schemas/WebhookEmailUnsubscribedPayload' headers: $ref: '#/components/schemas/LoopsWebhookHeaders' emailResubscribed: name: emailResubscribed title: Email resubscribed summary: Sent when a recipient resubscribes to marketing email from an email's preference center ("Unsubscribe" link). Not available for transactional emails because unsubscribe links are not included for transactional emails. Contains `sourceType` and the related `campaignId` or `loopId` (workflow ID), plus `contactIdentity` and `email` objects. contentType: application/json payload: $ref: '#/components/schemas/WebhookEmailResubscribedPayload' headers: $ref: '#/components/schemas/LoopsWebhookHeaders' emailOpened: name: emailOpened title: Email opened summary: Sent when a campaign or workflow email is opened. Not available for transactional emails because email opens are not tracked for transactional emails. Contains `sourceType` and the related `campaignId` or `loopId` (workflow ID), plus `contactIdentity` and `email` objects. contentType: application/json payload: $ref: '#/components/schemas/WebhookEmailOpenedPayload' headers: $ref: '#/components/schemas/LoopsWebhookHeaders' emailSpamReported: name: emailSpamReported title: Email spam reported summary: Sent when a recipient reports your email as spam. Contains `sourceType` and the related `campaignId`, `loopId` (workflow ID), or `transactionalId`, plus `contactIdentity` and `email` objects. contentType: application/json payload: $ref: '#/components/schemas/WebhookEmailSpamReportedPayload' headers: $ref: '#/components/schemas/LoopsWebhookHeaders' testingTestEvent: name: testingTestEvent title: Test event summary: Test event that can be triggered from the Webhooks settings page in Loops. Use this to verify that your endpoint is working and that your signature verification code is correct. The payload contains `eventName`, `eventTime`, `message` (always `"test"`), and `webhookSchemaVersion`. contentType: application/json payload: $ref: '#/components/schemas/WebhookTestingTestEventPayload' headers: $ref: '#/components/schemas/LoopsWebhookHeaders' schemas: WebhookBasePayload: type: object required: - eventName - eventTime - webhookSchemaVersion - contactIdentity properties: eventName: type: string eventTime: type: integer description: Unix timestamp in seconds. webhookSchemaVersion: type: string const: 1.0.0 contactIdentity: $ref: '#/components/schemas/WebhookContactIdentity' WebhookCampaignEmailSentPayload: allOf: - $ref: '#/components/schemas/WebhookBasePayload' - type: object required: - eventName - campaignId - email - mailingLists - campaignName properties: eventName: type: string const: campaign.email.sent campaignId: type: string campaignName: type: string email: $ref: '#/components/schemas/WebhookEmail' mailingLists: type: array description: The mailing list(s) the email was sent to, or an empty array. items: $ref: '#/components/schemas/WebhookMailingList' WebhookContact: type: object required: - id - email - firstName - lastName - source - subscribed - userGroup - userId - mailingLists - optInStatus properties: id: type: string email: type: string firstName: type: - string - 'null' lastName: type: - string - 'null' source: type: string subscribed: type: boolean userGroup: type: string userId: type: - string - 'null' notes: type: - string - 'null' mailingLists: type: object description: An object of mailing list IDs and boolean subscription statuses. additionalProperties: type: boolean optInStatus: type: - string - 'null' description: Double opt-in status. enum: - accepted - pending - rejected - null additionalProperties: true description: Full contact object, including custom properties. WebhookContactCreatedPayload: allOf: - $ref: '#/components/schemas/WebhookBasePayload' - type: object required: - eventName - contact properties: eventName: type: string const: contact.created contact: $ref: '#/components/schemas/WebhookContact' WebhookContactDeletedPayload: allOf: - $ref: '#/components/schemas/WebhookBasePayload' - type: object required: - eventName properties: eventName: type: string const: contact.deleted WebhookContactIdentity: type: object required: - id - email - userId properties: id: type: string email: type: string userId: type: - string - 'null' WebhookContactMailingListSubscribedPayload: allOf: - $ref: '#/components/schemas/WebhookBasePayload' - type: object required: - eventName - mailingList properties: eventName: type: string const: contact.mailingList.subscribed mailingList: $ref: '#/components/schemas/WebhookMailingList' WebhookContactMailingListUnsubscribedPayload: allOf: - $ref: '#/components/schemas/WebhookBasePayload' - type: object required: - eventName - mailingList properties: eventName: type: string const: contact.mailingList.unsubscribed mailingList: $ref: '#/components/schemas/WebhookMailingList' WebhookContactUnsubscribedPayload: allOf: - $ref: '#/components/schemas/WebhookBasePayload' - type: object required: - eventName properties: eventName: type: string const: contact.unsubscribed WebhookEmail: type: object required: - id - emailMessageId - subject properties: id: type: string description: The ID of the email. emailMessageId: type: string description: The ID of the sent version of the campaign, workflow or transactional email. subject: type: string description: The subject of the email. WebhookEmailClickedPayload: allOf: - $ref: '#/components/schemas/WebhookMarketingEmailMetricPayload' - type: object required: - eventName properties: eventName: type: string const: email.clicked WebhookEmailDeliveredPayload: allOf: - $ref: '#/components/schemas/WebhookEmailMetricPayload' - type: object required: - eventName properties: eventName: type: string const: email.delivered WebhookEmailHardBouncedPayload: allOf: - $ref: '#/components/schemas/WebhookEmailMetricPayload' - type: object required: - eventName properties: eventName: type: string const: email.hardBounced WebhookEmailMetricPayload: allOf: - $ref: '#/components/schemas/WebhookBasePayload' - type: object required: - email - sourceType properties: email: $ref: '#/components/schemas/WebhookEmail' sourceType: type: string enum: - loop - campaign - transactional description: The type of email this event relates to. Workflow emails use `loop`. campaignId: type: string description: The ID of the campaign if `sourceType` is `campaign`. loopId: type: string description: The ID of the workflow if `sourceType` is `loop`. transactionalId: type: string description: The ID of the transactional email if `sourceType` is `transactional`. WebhookEmailOpenedPayload: allOf: - $ref: '#/components/schemas/WebhookMarketingEmailMetricPayload' - type: object required: - eventName properties: eventName: type: string const: email.opened WebhookEmailResubscribedPayload: allOf: - $ref: '#/components/schemas/WebhookMarketingEmailMetricPayload' - type: object required: - eventName properties: eventName: type: string const: email.resubscribed WebhookEmailSoftBouncedPayload: allOf: - $ref: '#/components/schemas/WebhookEmailMetricPayload' - type: object required: - eventName properties: eventName: type: string const: email.softBounced WebhookEmailSpamReportedPayload: allOf: - $ref: '#/components/schemas/WebhookEmailMetricPayload' - type: object required: - eventName properties: eventName: type: string const: email.spamReported WebhookEmailUnsubscribedPayload: allOf: - $ref: '#/components/schemas/WebhookMarketingEmailMetricPayload' - type: object required: - eventName properties: eventName: type: string const: email.unsubscribed WebhookLoopEmailSentPayload: allOf: - $ref: '#/components/schemas/WebhookBasePayload' - type: object required: - eventName - loopId - email - mailingLists - loopName properties: eventName: type: string const: loop.email.sent loopId: type: string description: The ID of the workflow. loopName: type: string description: The name of the workflow. email: $ref: '#/components/schemas/WebhookEmail' mailingLists: type: array description: The mailing list(s) the email was sent to, or an empty array. items: $ref: '#/components/schemas/WebhookMailingList' WebhookMailingList: type: object description: Details of the mailing list. required: - id - name - description - isPublic properties: id: type: string name: type: string description: type: - string - 'null' isPublic: type: boolean WebhookMarketingEmailMetricPayload: allOf: - $ref: '#/components/schemas/WebhookBasePayload' - type: object required: - email - sourceType properties: email: $ref: '#/components/schemas/WebhookEmail' sourceType: type: string enum: - loop - campaign description: The type of email this event relates to. Workflow emails use `loop`. Not available for transactional emails. campaignId: type: string description: The ID of the campaign if `sourceType` is `campaign`. loopId: type: string description: The ID of the workflow if `sourceType` is `loop`. WebhookTestingTestEventPayload: type: object required: - eventName - eventTime - message - webhookSchemaVersion properties: eventName: type: string const: testing.testEvent eventTime: type: integer description: Unix timestamp in seconds. message: type: string const: test webhookSchemaVersion: type: string const: 1.0.0 WebhookTransactionalEmailSentPayload: allOf: - $ref: '#/components/schemas/WebhookBasePayload' - type: object required: - eventName - transactionalId - email - transactionalName properties: eventName: type: string const: transactional.email.sent transactionalId: type: string transactionalName: type: string email: $ref: '#/components/schemas/WebhookEmail' LoopsWebhookHeaders: type: object description: Signature headers Loops sends with every webhook request. Verify by HMAC-SHA256 over `{webhook-id}.{webhook-timestamp}.{raw-body}` using the base64-decoded portion of the signing secret after the underscore. properties: webhook-id: type: string description: Unique event id. webhook-timestamp: type: string description: Unix timestamp of the send. webhook-signature: type: string description: Space-separated list of versioned base64 signatures. required: - webhook-id - webhook-timestamp - webhook-signature