Skip to main content

Lifecycle Webhooks

mittwald uses lifecycle events to notify the the external application of events that concern it.

Templating of Webhook URLs

You may use placeholders when configuring the URL of webhooks. The mStudio replaces them with the values of the attributes of the webhook payload.

You may use the following placeholders:

  • apiVersion: The API version used for the webhook. Currently always v1. See API versioning.
  • contributorId: ID of the contributor
  • extensionId: ID of the extension
  • extensionInstanceId: ID of the extension instance
  • contextId: ID of the extension context, eg. the ProjectID
  • context: The extension context kind the extension instance was added to. Currently one of customer and project.

Webhook Payloads

ExtensionAddedToContext

  • apiVersionstring
    required
  • kind"ExtensionAddedToContext"
    required
  • idstring (uuid)
    required
  • contextobject
    required
    • idstring (uuid)
      required
    • kindstring (one of: customer, project)
      required
  • consentedScopesarray of string
    required
    • Array[
    • *string
    • ]
  • stateobject
    required
    • enabledboolean
  • metaobject
    required
    • createdAtstring (date-time)
      required
  • secretstring
    required
  • requestobject
    required
    • idstring (uuid)
      required
    • createdAtstring (date-time)
      required
    • targetobject
      required
      • methodstring
        required
      • urlstring (uri)
        required
  • ExtensionInstanceUpdated

    • apiVersionstring
      required
    • kind"ExtensionInstanceUpdated"
      required
    • idstring (uuid)
      required
    • contextobject
      required
      • idstring (uuid)
        required
      • kindstring (one of: customer, project)
        required
    • consentedScopesarray of string
      required
      • Array[
      • *string
      • ]
  • stateobject
    required
    • enabledboolean
  • metaobject
    required
    • createdAtstring (date-time)
      required
  • requestobject
    required
    • idstring (uuid)
      required
    • createdAtstring (date-time)
      required
    • targetobject
      required
      • methodstring
        required
      • urlstring (uri)
        required
  • ExtensionInstanceSecretRotated

    • apiVersionstring
      required
    • kind"ExtensionInstanceSecretRotated"
      required
    • idstring (uuid)
      required
    • contextobject
      required
      • idstring (uuid)
        required
      • kindstring (one of: customer, project)
        required
    • secretstring
      required
    • requestobject
      required
      • idstring (uuid)
        required
      • createdAtstring (date-time)
        required
      • targetobject
        required
        • methodstring
          required
        • urlstring (uri)
          required

    ExtensionInstanceRemovedFromContext

    • apiVersionstring
      required
    • kind"ExtensionInstanceUpdated"
      required
    • idstring (uuid)
      required
    • contextobject
      required
      • idstring (uuid)
        required
      • kindstring (one of: customer, project)
        required
    • consentedScopesarray of string
      required
      • Array[
      • *string
      • ]
  • stateobject
    required
    • enabledboolean
  • metaobject
    required
    • createdAtstring (date-time)
      required
  • requestobject
    required
    • idstring (uuid)
      required
    • createdAtstring (date-time)
      required
    • targetobject
      required
      • methodstring
        required
      • urlstring (uri)
        required
  • Explanation of the Individual Attributes

    apiVersion

    The API version that is used for the webhook call. Currently always v1. Enables breaking changes of the payload in the future. For more information, see api versioning.

    kind

    The lifecycle webhook kind. Possible values:

    • ExtensionAddedToContext
    • ExtensionInstanceUpdated
    • ExtensionInstanceSecretRotated
    • ExtensionInstanceRemovedFromContext

    Depending on requirements, you can use the same URL for all webhooks and use this attribute to discern the different kinds of webhooks. Alternatively, you can use different webhook endpoints for each kind of webhook and ignore this attribute. For more information about the semantics of the individual lifecycle webhooks, see lifecycle webhooks concept.

    id

    The globally unique identifier of the extension instance. Don't confuse this with the extension ID that identifies the extension as a whole. Also, you must not confuse this ID with the request.id that identifies the request and can be used to mitigate replay attacks. For more information, see request meta information.

    context

    Information about the extension context, the extension instance belongs to. Currently, the two possible extension contexts are:

    • customer
    • project

    Additionally to the context.kind, the mStudio transmits the context.id. This is the ID of the extension context, so the ID of a project, for example.

    consentedScopes

    The scopes the user consented to when they added the extension to an extension context. If the contributor of an extension adds additional scopes to it later, the user has to consent to those scopes before the extension can use those scopes for that extension instance.

    The extension can check whether the required scopes are available using the consentedScopes. For this reason, we recommend persisting the consentedScopes.

    state

    The current state of the extension instance. The mStudio currently only informs the external application of the state of enabled via the lifecycle webhooks. This tells the extension whether an extension instance is active and, therefore, whether it should function. The authentication and authorization mechanisms don't work for deactivated extension instances. In that case, domain actions of that extension instance are not accepted.

    meta

    Meta information about the extension instance. Currently, the mStudio only sends the creation date of the extension instance. This should not be confused with the request meta information which describes the webhook call.

    secret

    The secret that is used for the authentication of the extension instance. You may use this to implement user-independent authentication. For more information, see authentication of extension instances.

    request

    Meta information about the lifecycle webhook request.

    You can mitigate attack vectors such as replay or forward replay attacks by validating the request property.

    You can persist the request.id and compare it with the IDs of future lifecycle webhooks to only accept the same request once. The mStudio never sends requests with the same request.id multiple times.

    You can validate the field request.target.url to check whether the request reached the correct receiver. This way, you can avoid processing signed requests that an attacker forwarded to another extension.

    You can validate the field request.createdAt to identify old requests. Keep in mind that the mStudio potentially sends webhooks asynchronously and the time difference between the webhook creation and receiving the webhook may vary.

    Validation of Lifecycle Webhooks

    We offer the possibility to confirm that mittwald sent the webhook and nobody corrupted the request body. For this, we sign the request body and send the signature, including meta information in the request headers.

    X-Marketplace-Signature-Serial: 7f640dcf-c5fb-4e79-bc4b-99a30e50fcc5
    X-Marketplace-Signature-Algorithm: Ed25519
    X-Marketplace-Signature: HKdS7xD...qQMN94HINte7Dlof+9V/PQ6gW0C5uBOz+9F6YNOLE6vdb2ybVwRH23GAg==

    X-Marketplace-Signature-Serial

    Unique ID of the public key you must use to verify the signature. We guarantee one stable public key for every public key ID. Therefore, you may cache the public key. You can get the public keys with the operation GET/v2/webhook-public-keys/{serial}/.

    X-Marketplace-Signature-Algorithm

    Algorithm, the mStudio used to generate the signature. Currently always Ed25519.

    X-Marketplace-Signature

    DSA signature encoded in base64.

    You can use common cryptographic libraries in your preferred programming language to verify the signature. The complete and unprocessed request body has to be verified using the verify method. This ensures that the request body was transmitted by mittwald and was not modified by a third party.

    bodyBytes, err := io.ReadAll(body)
    if err != nil {
    return err
    }

    if !ed25519.Verify(publicKey, bodyBytes, signature) {
    panic("invalid signature")
    }

    Reference Implementations of the Validation of Lifecycle Webhooks

    TODO

    Dry Running Webhooks

    An extension developer can trigger a webhook in dry run mode to make the extension development easier. The developer defines the webhook kind for the dry run webhook execution, and the mStudio then calls the corresponding extension webhook. The mStudio calls the webhook endpoint with demo values.
    After the dry run webhook call is completed, the mStudio returns the result of the webhook call in the API response. This includes the response body, the response headers, and general errors.
    The mStudio includes the query parameter dry-run=true in dry-run webhook calls so that extensions can identify them. If the dry-run query parameter is set to true in a webhook call, the extension backend should handle the webhook call as such and interpret the webhook as a test. The mStudio additionally sets the query parameter executing-user-id for dry run webhook calls.

    For dry run webhook calls, the extensionInstanceId does not have any relevant meaning for real entities in the mStudio.

    Only users that belong to a contributor are allowed to trigger dry run webhooks.

    Demo Values

    You can use the demo values as test data to test the internal logic of your extension by sending the following JSON object in the body. If the values are not set, the mStudio derives them from the extension or generates random values.

    • contextIdstring
    • scopesarray of string
      • Array[
      • *string
      • ]
  • instandeDisabledboolean
  • createdAtstring (date-time)
  • secretstring