Skip to content
Last update: January 26, 2024

Webhooks

Introduction

Webhooks are a way for the custodian to communicate with MMI’s backend. MMI’s backend must keep track of transactions and signed messages for the following reasons:

  1. Providing updates of the status of transactions to the extension
  2. Billing of users, which is based on the AUD of custodial addresses.
  3. Tracking of activation, engagement, retention metrics
  4. Broadcasting transactions in the event that the custodian is not broadcasting them

MMI provides custodians with a URL, and credentials, and custodians are required to request these URLs with those credentials when the status of a transaction or signed message changes.

For example:

  1. When a transaction is create
  2. When a transaction is rejected or aborted
  3. When a transaction is complete
  4. When a signed message is signed
  5. When a signed message is rejected

The specification for webhooks is given in this appendix.

MMI will provide the custodian with the URL and credentials to use when making requests to MMI’s backend.

Webhook Specification

Endpoint: /v2/webhook/custodian

 class WebhookRequest {
  transaction?: {
    id: string // This must match the transaction ID from the custodian_createTransaction method
    type: string // In hexlified (0x...) format
    from: string // Hexlified
    to: string // Contract address or recipient (hexlified)
    value: string // Can be null, 0x, 0x0 etc
    gas: string // Hexlified
    gasPrice?: string // Hexlified
    maxFeePerGas?: string // Hexlified
    maxPriorityFeePerGas?: string // Hexlified
    signedRawTransaction?: string | null // The RLP-encoded signed transaction in hexlified format if the transaction is signed
    nonce: string // Hexlified
    data: string //  Can be null, 0x, 0x0 etc
    hash: string
    status: {
      finished: boolean  // This transaction is finished - it has failed, or succeeded (been mined). There will be no more webhooks
      submitted: boolean // The transaction has been submitted to the blockchain
      signed: boolean // The transaction has been signed, and the nonce/hash is available
      success: boolean // The transaction was successful, or is successful so far. 
      displayText: string // Text which is displayed to users
      reason: string // The reason for the transaction status - for example, the failure reason
    }
  }
  signedMessage?: {
    id: string // This must match the signature ID from the custodian_sign or custodian_signTypeData method
    address: string // Hexlified
    signatureVersion: string // v3, v4 or personalSign
    signature: string, // The hexlified signature
    status: {
      finished: boolean
      signed: boolean
      success: boolean
      displayText: string
      reason: string // The reason for the transaction status
    }
  },
  metadata: {
      userId: string // This must match the `sub` claim of the customer proof of the user who created the transaction
      customerId: string // If users are part of some group, e.g. an organisation or fund, a unique ID for this organisation or fund (used for billing)
      customerName: string // If users are part of some group, e.g. an organisation or fund, a human readable name for this organisation or fund (used for billing)
      chainId?: string // In hexlified (0x...) format. Not usually included for signed messages
      originUrl: string // As passed from the extension when the transaction was created
      transactionCategory: string // As passed from the extension when the transaction was created
      note: string // As passed from the extension when the transaction was created
      traceId?: string // An ID for the webhook, which could be used for debugging
      rpcUrl?: string // // The URL to which the transaction will be published. This can be omitted if none was originally sent or if the custodian does not wish to share this information
      custodianPublishesTransaction?: boolean // Whether the custodian was requested to publish/broadcast the transaction - present in the original transaction parameters n 
      isTest?: boolean // Whether this is a test transaction or should otherwise be ignored for billing purposes
  }
}
  • During development, we will provide a specific environment for the custodian as a sandbox for webhooks.

  • We assume that custodians are maintaining a separate dev or staging environment which triggers webhooks in our staging environment.

Headers

Content-type: application/json

Authorization: Bearer <your oauth token>

Obtaining the Oauth token

We provide the following:

  • Client ID.
  • Client secret.
  • Auth audience.
  • Token endpoint.
  • Webhook endpoint.

Sample token request

curl --request POST \                                                                  
  --url $TOKEN_ENDPOINT \
  --header 'content-type: application/json' \
  --data '{"client_id":"$CLIENT_ID",,"client_secret":"$CLIENT_SECRET","audience": "$AUTH_AUDIENCE" grant_type":"client_credentials"}'

Sample token response

{"access_token":"<your-access-token>","expires_in":86400,"token_type":"Bearer"} 
  • Tokens last one day by default.
  • There needs to be a mechanism to obtain new ones after they expire.
  • The token is a JWT, so you can introspect the token and look for the exp property before using it. This is the epoch timestamp after which the token is expired.