Webhooks

In case you want to perform your own actions when events happen in Moonbase, you can configure webhooks to forward events to your own APIs. Webhooks can be set up to trigger on a number of different events:

  • Payment events
    • OrderPaid
    • OrderCompleted
    • OrderPayoutScheduled
    • OrderRefunded
  • Licensing events
    • LicenseActivated
    • LicenseProvisioned
    • TrialActivated
  • Product release events
    • ProductReleasePublished
  • Subscription events
    • SubscriptionStarted
    • SubscriptionRenewed
    • SubscriptionExpired
    • SubscriptionCancelled
    • SubscriptionCompleted
  • Voucher events
    • VoucherCodeRedeemed
  • Customer events
    • CustomerSignedUp
    • CustomerSubscribed
    • CustomerUnsubscribed

To get started with webhooks, head over to your Moonbase account settings, and set up your first webhook:

Once created, it's immediately active and you will have access to the secret key used to compute the signature as described in Security.

Do you have a specific scenario you need webhooks for that is not covered by the current events and content?
Reach out to us through the support channel, or at developers@moonbase.sh.

Security

To ensure that the webhook requests are in fact originating from Moonbase and not some malicious actor, we apply a HMAC-SHA256 algorithm on the body of the request and include this signature in a X-Signature header on the request.

Example signature verification code

    using var algorithm = new HMACSHA256(Encoding.UTF8.GetBytes(secretKey));
    var json = await request.Content.ReadAsStringAsync();
    var hash = algorithm.ComputeHash(Encoding.UTF8.GetBytes(json));
    var signature = Convert.ToBase64String(hash).ToUpperInvariant();
    var sentSignature = request.Headers.GetValues("X-Signature").Single();
    Assert.Equal(sentSignature, signature);

Should you ever need to change the secret key, you can do so by editing the webhook.

JSON structure

All webhook requests being sent by Moonbase will have a fixed structure with some basic details about the event that occurred. Additionally, relevant resources are also attached, so that you don't have to query the Moonbase API to fetch more details. The shape of these resources differ between events, and are described on this page.

  • Name
    id
    Type
    string
    Description

    Identifier of this unique webhook request.

  • Name
    endpoint
    Type
    string
    Description

    The endpoint the request is being sent to.

  • Name
    eventType
    Type
    enum
    Description

    The event being communicated, will one of the events listed in this article.

  • Name
    timestamp
    Type
    datetime
    Description

    ISO-8601 date and time of the time at which the event occurred.

  • Name
    resource
    Type
    object
    Description

    The resource that the event relates to:

    • Name
      type
      Type
      enum
      Description

      One of: Order, License, Trial, ProductRelease, Subscription, VoucherCode, Customer

    • Name
      data
      Type
      object
      Description

      The actual contents of the entity. See below for more details about how this is structured.

  • Name
    customer
    Type
    object
    Nullability
    nullable
    Description

    Details about the customer associated with the event. In case the customer is unknown, it will be null.

    • Name
      id
      Type
      string
      Description

      Unique identifier of the customer.

    • Name
      name
      Type
      string
      Description

      Name of the customer.

    • Name
      email
      Type
      string
      Description

      Email address of the customer.

    • Name
      isDeleted
      Type
      boolean
      Description

      Flag showing if the customer account was deleted

    • Name
      properties
      Type
      object
      Optionality
      optional
      Description

      Any custom properties configured on the customer, keyed by name. Omitted when the customer has none. See Customer events for the shape of each value.

Webhook request

{
    "id": "a885619a-cb5e-41a6-a07b-1be4e520cccb",
    "endpoint": "https://api.my-domain.example/webhook",
    "eventType": "OrderCompleted",
    "timestamp": "2024-11-11T11:11:11.0000000Z",
    "resource": {
        "type": "Order",
        "data": {...}
    },
    "customer": {
        "id": "92c0f540-9044-4b00-af4c-fe29f5da355e",
        "name": "Example customer name",
        "email": "user@example.com",
        "isDeleted": false
    }
}

Payment Events

When customers make purchases through Moonbase, they make them on what we call an "Order". This is why the events related to payments are all prefixed with Order, and the typical lifecycle of an order goes like this:

  1. The customer pays for an order, a OrderPaid event is sent (not applicable for free purchases)
  2. Moonbase fulfills the order, issuing licenses and sending receipts, completing the order, a OrderCompleted event is sent
  3. Moonbase then calculates all affiliate revenue splits and schedules payout for the order, a OrderPayoutScheduled event is sent

Lastly, if you refund the order, Moonbase will emit a OrderRefunded event to any webhook listening. The same event is sent for both full and partial refunds: check isFullyRefunded and isPartiallyRefunded to tell them apart, and read the refundHistory array for the amounts and line items covered by each refund.

All events concering orders will have the same shape of the order:

  • Name
    id
    Type
    string
    Description

    Unique identifier of the order.

  • Name
    status
    Type
    enum(Paid|Completed)
    Description

    The current status of the order, Paid if not yet completed, Completed if paid and fulfilled.

  • Name
    currency
    Type
    string
    Description

    The currency used for this purchase.

  • Name
    completedAt
    Type
    DateTime
    Optionality
    optional
    Description

    The timestamp for when the order was completed.

  • Name
    refundedAt
    Type
    DateTime
    Optionality
    optional
    Description

    The timestamp for when the order was fully refunded. Omitted while the order is not fully refunded.

  • Name
    isFullyRefunded
    Type
    boolean
    Description

    Whether the entire order has been refunded. Remains false for a partial refund, even though refundHistory lists the refunded amounts.

  • Name
    isPartiallyRefunded
    Type
    boolean
    Description

    Whether the order has been partially but not fully refunded. true once at least one refund has settled while the order still has unrefunded units.

  • Name
    refundHistory
    Type
    array
    Optionality
    optional
    Description

    The settled refunds applied to this order, present once at least one refund has settled. Each entry describes a single refund:

    • Name
      id
      Type
      string
      Description

      Unique identifier of the refund.

    • Name
      at
      Type
      DateTime
      Description

      The timestamp for when this refund was made.

    • Name
      amount
      Type
      object
      Description

      The amount refunded to the customer in this refund, containing:

      • currency: The currency of the refunded amount.
      • amount: The refunded amount.
    • Name
      lineItems
      Type
      array
      Description

      The order line items covered by this refund, each containing:

      • Name
        lineItemId
        Type
        string
        Description

        Identifier of the refunded order line item. Matches the lineItemId of an entry in the order's items array.

      • Name
        quantity
        Type
        number
        Description

        The number of units refunded for this line item in this refund.

      • Name
        amount
        Type
        object
        Description

        The amount refunded to the customer for this line item, with the same currency and amount properties as above.

  • Name
    isDisputed
    Type
    boolean
    Description

    Flag indicating if this purchase has been disputed.

  • Name
    total
    Type
    object
    Description

    Calculated total for all items part of the order, contains:

    • original: The original amount before discounts
    • discount: The total amount of discounts added
    • subtotal: The total amount after discounts have been removed
    • taxes: Total taxes to pay, may be inclusive or exclusive of the subtotal depending on configuration, currency and region.
    • due: How much is being paid in total by the customer
  • Name
    payout
    Type
    object
    Optionality
    optional
    Description

    Payout details for this order, only present if an amount is to be paid. Note that this may get updated as affiliate revenue splits are calculated later in the pipeline.

    • subtotal: The original amount paid by the customer
    • taxes: The total amount of taxes collected and remitted
    • platformFees: The total amount of fees going to Moonbase
    • due: How much is being paid out to you
  • Name
    billingDetails
    Type
    object
    Description

    Billing details about the customer who made the purchase:

    • Name
      name
      Type
      string
      Description

      Full name of the customer.

    • Name
      businessName
      Type
      string
      Nullability
      nullable
      Description

      In case the purchase was a business purchase, this field will have the name.

    • Name
      taxId
      Type
      string
      Nullability
      nullable
      Description

      In case the purchase was a business purchase, this field will have the tax id if given.

    • Name
      email
      Type
      string
      Description

      Email address of the customer.

    • Name
      phone
      Type
      string
      Nullability
      nullable
      Description

      Phone number of the customer. Note that collecting phone numbers is an optional feature in Moonbase and not activated by default.

    • Name
      address
      Type
      object
      Nullability
      nullable
      Description

      Billing address for the customer, not present if the purchase was a lead generator purchase. Contains the following properties:

      • Name
        countryCode
        Type
        string
        Description

        ISO 3166-1 alpha-2 two-letter country code.

      • Name
        streetAddress1
        Type
        string
        Description

        First line of the regular street address.

      • Name
        streetAddress2
        Type
        string
        Optionality
        optional
        Description

        Second line of the regular street address.

      • Name
        postCode
        Type
        string
        Description

        Postal code of the address.

      • Name
        locality
        Type
        string
        Description

        Locality of the address, only required if no region is given.
        Also known as City.

      • Name
        region
        Type
        string
        Description

        Region of the address, only required if no locality is given.
        Also known as State.

  • Name
    items
    Type
    array
    Description

    Collection of items part of the order. Note that this can be either products or bundles, and they are discriminated using the type property. Some properties are present on both types, others are shared:

      Product line item

    • Name
      type
      Type
      'Product'
      Description

      Item type discriminator.

    • Name
      productId
      Type
      string
      Description

      The unique ID of the product.

      Bundle line item

    • Name
      type
      Type
      'Bundle'
      Description

      Item type discriminator.

    • Name
      bundleId
      Type
      string
      Description

      The unique ID of the bundle.

      Shared

    • Name
      lineItemId
      Type
      string
      Description

      Stable identifier of this order line item. Use it to correlate entries in refundHistory back to the line they refunded.

    • Name
      quantity
      Type
      number
      Description

      The quantity of this item.

    • Name
      price
      Type
      object
      Description

      The original price of the product or bundle.

    • Name
      total
      Type
      object
      Description

      The calculated total for this line item.

    • Name
      variation
      Type
      object
      Description

      Details about the selected pricing variation for this item.

    • Name
      fulfillment
      Type
      object
      Optionality
      optional
      Description

      Details about the license fulfillment for this purchase.

Order data example

{
    "id": "dc0b53f9-4e43-4179-8554-00f2a8228a25",
    "status": "Completed",
    "currency": "EUR",
    "completedAt": "2024-11-11T11:11:11.0000000Z",
    "isFullyRefunded": false,
    "isPartiallyRefunded": false,
    "isDisputed": false,
    "total": {
        "original": {
            "currency": "EUR",
            "amount": 10
        },
        "discount": {
            "currency": "EUR",
            "amount": 5
        },
        "subtotal": {
            "currency": "EUR",
            "amount": 5
        },
        "taxes": {
            "currency": "EUR",
            "amount": 5
        },
        "due": {
            "currency": "EUR",
            "amount": 5
        }
    },
    "billingDetails": {
        "name": "Example User",
        "businessName": null,
        "taxId": null,
        "email": "user@example.com",
        "phone": null,
        "address": {
            "countryCode": "NO",
            "streetAddress1": "Utsikten 6",
            "streetAddress2": null,
            "locality": "Skien",
            "region": null,
            "postCode": "3718"
        }
    },
    "couponsApplied": [
        {
            "id": "43396b94-53b4-4901-8e9e-ab1d6ee7d3b5",
            "code": "MY-UNIQUE-CODE",
            "name": "Half off coupons",
            "description": "Thanks for being our customer!",
            "discount": {
                "type": "PercentageOffDiscount",
                "percentage": 0.5
            },
            "applicableProductVariations": {},
            "applicableBundleVariations": {}
        }
    ],
    "items": [
        {
            "type": "Product",
            "lineItemId": "7c1f0b2a-9d3e-4a5b-8c6d-0e1f2a3b4c5d",
            "productId": "example-product",
            "quantity": 1,
            "price": {
                "EUR": 10
            },
            "total": {
                "original": {
                    "currency": "EUR",
                    "amount": 10
                },
                "discount": {
                    "currency": "EUR",
                    "amount": 5
                },
                "subtotal": {
                    "currency": "EUR",
                    "amount": 5
                },
                "due": {
                    "currency": "EUR",
                    "amount": 5
                }
            },
            "variation": {
                "id": "v802c5",
                "name": "Default",
                "entitlement": {
                    "type": "PerpetualLicense"
                },
                "recurrence": {
                    "type": "OneOff"
                },
                "price": {
                    "EUR": 10
                }
            },
            "fulfillment": {
                "type": "License",
                "licenseIds": [
                    "d564a47a-d7f3-48ea-8698-57ad7512b11d"
                ]
            }
        }
    ]
}

When an order is only partially refunded, isPartiallyRefunded is true while isFullyRefunded stays false, no order-level refundedAt is set, and the refundHistory array carries the details of each settled refund:

Order data example (partially refunded)

{
    "id": "dc0b53f9-4e43-4179-8554-00f2a8228a25",
    "status": "Completed",
    "currency": "EUR",
    "completedAt": "2024-11-11T11:11:11.0000000Z",
    "isFullyRefunded": false,
    "isPartiallyRefunded": true,
    "isDisputed": false,
    "total": {
        "original": { "currency": "EUR", "amount": 20 },
        "discount": { "currency": "EUR", "amount": 0 },
        "subtotal": { "currency": "EUR", "amount": 20 },
        "taxes": { "currency": "EUR", "amount": 5 },
        "due": { "currency": "EUR", "amount": 20 }
    },
    "refundHistory": [
        {
            "id": "b3f1c2a4-6d8e-4f51-9c7a-1d2e3f4a5b6c",
            "at": "2024-11-12T09:30:00.0000000Z",
            "amount": { "currency": "EUR", "amount": 10 },
            "lineItems": [
                {
                    "lineItemId": "8f2c1b4e-3a6d-4f51-9c7a-1d2e3f4a5b6c",
                    "quantity": 1,
                    "amount": { "currency": "EUR", "amount": 10 }
                }
            ]
        }
    ],
    "items": [
        {
            "type": "Product",
            "lineItemId": "8f2c1b4e-3a6d-4f51-9c7a-1d2e3f4a5b6c",
            "productId": "example-product",
            "quantity": 2,
            "price": { "EUR": 10 },
            "total": {
                "original": { "currency": "EUR", "amount": 20 },
                "discount": { "currency": "EUR", "amount": 0 },
                "subtotal": { "currency": "EUR", "amount": 20 },
                "due": { "currency": "EUR", "amount": 20 }
            }
        }
    ]
}

Licensing events

We currently expose the following licensing related events:

  • LicenseActivated: Happens the first time a license is activated on any device
  • LicenseProvisioned: Happens when a license is provisioned for a customer, for example after an order completes, a voucher is redeemed, or a subscription is migrated. Not sent for imported licenses.
  • TrialActivated: Happens the first time a trial is started on any device

These events have different resource shapes:

License events

Both LicenseActivated and LicenseProvisioned carry the same license resource shape:

  • Name
    id
    Type
    string
    Description

    Unique identifier of the license.

  • Name
    externalId
    Type
    string
    Optionality
    optional
    Description

    Your own external identifier for the license, if one was set. Omitted when not set.

  • Name
    status
    Type
    enum(Active|Revoked|Expired|Pending)
    Description

    The current status of the license.

  • Name
    productId
    Type
    string
    Description

    The product ID that the license is for.

  • Name
    maxNumberOfActivations
    Type
    number
    Description

    The maximum number of devices the license can be activated on.

  • Name
    offlineActivationsAllowed
    Type
    boolean
    Description

    Whether the license is allowed to be activated offline.

  • Name
    properties
    Type
    object
    Optionality
    optional
    Description

    Custom properties configured on the license, keyed by name. Omitted when the license has no custom properties. Each value contains:

    • Name
      type
      Type
      enum(text|number|date|boolean|object|array)
      Description

      The type of the property value.

    • Name
      value
      Description

      The property value, shaped according to type.

    • Name
      public
      Type
      boolean
      Description

      Whether the property is exposed publicly.

    • Name
      includeInToken
      Type
      boolean
      Description

      Whether the property is included in issued license tokens.

License data example

{
    "id": "5ec722c7-f7d9-441d-83af-69ea634e429b",
    "externalId": "EXT-12345",
    "status": "Active",
    "productId": "example-product",
    "maxNumberOfActivations": 5,
    "offlineActivationsAllowed": false,
    "properties": {
        "deployment": {
            "type": "text",
            "value": "production",
            "public": false,
            "includeInToken": true
        }
    }
}

Trial events

  • Name
    id
    Type
    string
    Description

    Unique identifier of trial.

  • Name
    status
    Type
    enum(Active|Expired)
    Description

    The current status of the trial.

  • Name
    productId
    Type
    string
    Description

    The product ID being trialled.

  • Name
    lastValidatedAt
    Type
    DateTime
    Description

    The time of last validation.

  • Name
    expiresAt
    Type
    DateTime
    Description

    The time at which this trial expires.

Trial data example

{
    "id": "7cda66c7d03bd35bac892b97436c33b8",
    "status": "Active",
    "productId": "example-product",
    "lastValidatedAt": "2024-11-11T11:11:11.0000000Z",
    "expiresAt": "2025-11-11T11:11:11.0000000Z"
}

Product release events

We currently expose the following product release related events:

  • ProductReleasePublished: Happens every time a new version of a product is being released

Event

  • Name
    productId
    Type
    string
    Description

    The product ID of the released product

  • Name
    version
    Type
    string
    Description

    The version of the release

  • Name
    description
    Type
    string
    Nullability
    nullable
    Description

    The description of the release

  • Name
    publishedAt
    Type
    DateTime
    Description

    The time at which the product release was published

Product release data example

{
    "productId": "example-product",
    "version": "1.2.3",
    "description": "Enhanced performance and critical bug fixes",
    "publishedAt": "2024-11-11T11:11:11.0000000Z"
}

Subscription events

We currently expose the following subscription related events:

  • SubscriptionStarted: Happens every time a new subscription is started
  • SubscriptionRenewed: Happens every time a subscription is renewed
  • SubscriptionExpired: Happens every time a subscription expires
  • SubscriptionCancelled: Happens every time a subscription is cancelled
  • SubscriptionCompleted: Happens when a subscription reaches its natural completion, for example when a fixed-term subscription runs its course

Event

  • Name
    id
    Type
    string
    Description

    The unique ID of the subscription

  • Name
    status
    Type
    enum(Active|Cancelled|Expired|Completed)
    Description

    The current status of the subscription. Completed is used once the subscription has reached its natural completion.

  • Name
    expiresAt
    Type
    DateTime
    Description

    The time at which the subscription expires unless sucessfully renewed.

  • Name
    renewedAt
    Type
    DateTime
    Nullability
    nullable
    Description

    The last time at which the subscription was renewed, null if never renewed.

  • Name
    startedAt
    Type
    DateTime
    Description

    The time at which the subscription started originally.

  • Name
    total
    Type
    object
    Description

    Calculated total to be paid for each subscription cycle, contains:

    • original: The original amount before discounts
    • discount: The total amount of discounts added
    • subtotal: The total amount after discounts have been removed
    • taxes: Total taxes to pay, may be inclusive or exclusive of the subtotal depending on configuration, currency and region.
    • due: How much is being paid in total by the customer
  • Name
    cycleLength
    Type
    enum(Monthly|Yearly)
    Description

    The length of each subscription cycle on this subscription.

  • Name
    content
    Type
    object
    Description

    The content of the subscription, can either be a product or a bundle:

    Product content


    • Name
      type
      Type
      'Product'
      Description

      Content type discriminator.

    • Name
      productId
      Type
      string
      Description

      ID of the product.

    • Name
      quantity
      Type
      number
      Description

      The number of licenses being subscribed to.

    • Name
      fulfillment
      Type
      object
      Description

      The fulfillment details for this product, will contain the following properties:

      • Name
        licenseIds
        Type
        string[]
        Description

        A list of license IDs that has been fulfilled for this fulfillment.

    Bundle content


    • Name
      type
      Type
      'Bundle'
      Description

      Content type discriminator.

    • Name
      bundleId
      Type
      string
      Description

      ID of the bundle.

    • Name
      quantity
      Type
      number
      Description

      The number of licenses being subscribed to for each product in the bundle.

    • Name
      fulfillment
      Type
      object
      Description

      The fulfillment details for this bundle, will contain the following properties:

      • Name
        licenseIds
        Type
        string[]
        Description

        A list of license IDs that has been fulfilled for this fulfillment.

Subscription data example

{
    "id": "4e794c6a-56c3-4a2b-a568-a99eaa0d3b7f",
    "status": "Active",
    "expiresAt": "2025-02-14T21:26:45.3769978Z",
    "renewedAt": "2025-02-13T21:26:45.3769978Z",
    "startedAt": "2025-02-08T21:08:43.6530272Z",
    "total": {
        "original": {
            "currency": "EUR",
            "amount": 15
        },
        "discount": {
            "currency": "EUR",
            "amount": 0
        },
        "subtotal": {
            "currency": "EUR",
            "amount": 12.4
        },
        "taxes": {
            "currency": "EUR",
            "amount": 2.6
        },
        "due": {
            "currency": "EUR",
            "amount": 15
        }
    },
    "cycleLength": "Monthly",
    "content": {
        "type": "Product",
        "quantity": 1,
        "fulfillment": {
            "type": "License",
            "licenseIds": [
                "922c8100-e57e-48e6-9ea8-a0e00db52c13"
            ]
        },
        "productId": "example-product"
    }
}

Voucher events

We currently expose the following voucher related events:

  • VoucherCodeRedeemed: Happens every time a code of a voucher is redeemed by a customer

Event

  • Name
    code
    Type
    string
    Description

    The code being redeemed

  • Name
    redeemedBy
    Type
    string
    Description

    Unique ID of the customer that redeemed this code

  • Name
    redeemedAt
    Type
    datetime
    Description

    ISO-8601 date and time for when the redemption took place.

  • Name
    voucher
    Type
    object
    Description

    Details about the parent voucher object, including:

    • Name
      id
      Type
      string
      Description

      Unique ID of this voucher

    • Name
      name
      Type
      string
      Description

      Name as configured in your Moonbase account

    • Name
      description
      Type
      string
      Description

      Description as configured in your Moonbase account

    • Name
      redeemsProducts
      Type
      array
      Description

      List of products that this voucher redeems, each object in the list contains:

      • Name
        value
        Type
        string
        Description

        The ID of the product

      • Name
        quantity
        Type
        number
        Description

        Number of licenses provisioned per redemption

    • Name
      redeemsBundles
      Type
      array
      Description

      List of bundles that this voucher redeems, each object in the list contains:

      • Name
        value
        Type
        string
        Description

        The ID of the bundle

      • Name
        quantity
        Type
        number
        Description

        Number of licenses provisioned per redemption

Voucher data example

{
    "code": "EXAMPLE-VOUCHER-CODE",
    "redeemedBy": "922c8100-e57e-48e6-9ea8-a0e00db52c13",
    "redeemedAt": "2025-02-14T21:26:45.3769978Z",
    "voucher": {
        "id": "4e794c6a-56c3-4a2b-a568-a99eaa0d3b7f",
        "name": "Example voucher",
        "description": "This voucher is used for demo purposes",
        "redeemsProducts": [
            { "value": "example-product-id", "quantity": 1 },
        ],
        "redeemsBundles": [
            { "value": "example-bundle-id", "quantity": 1 },
        ],
    },
}

Customer events

We currently expose the following customer related events:

  • CustomerSignedUp: Happens when a customer creates a Moonbase account, either by signing up themselves or when an account is created for them during checkout or license provisioning. Not sent for imported customers or customers registered without an account.
  • CustomerSubscribed: Happens when a customer opts in to your newsletter and marketing communications.
  • CustomerUnsubscribed: Happens when a customer opts out of your newsletter and marketing communications.

All three events share the same customer resource shape:

  • Name
    id
    Type
    string
    Description

    Unique identifier of the customer.

  • Name
    name
    Type
    string
    Description

    Name of the customer.

  • Name
    email
    Type
    string
    Description

    Email address of the customer.

  • Name
    isDeleted
    Type
    boolean
    Description

    Flag showing if the customer account was deleted.

  • Name
    properties
    Type
    object
    Optionality
    optional
    Description

    Custom properties configured on the customer, keyed by name. Omitted when the customer has no custom properties. Each value contains:

    • Name
      type
      Type
      enum(text|number|date|boolean|object|array)
      Description

      The type of the property value.

    • Name
      value
      Description

      The property value, shaped according to type.

    • Name
      public
      Type
      boolean
      Description

      Whether the property is exposed publicly.

    • Name
      includeInToken
      Type
      boolean
      Description

      Whether the property is included in issued tokens.

Customer data example

{
    "id": "92c0f540-9044-4b00-af4c-fe29f5da355e",
    "name": "Example customer name",
    "email": "user@example.com",
    "isDeleted": false,
    "properties": {
        "industry": {
            "type": "text",
            "value": "Technology",
            "public": true,
            "includeInToken": false
        }
    }
}

Was this page helpful?