Event Types

See below for a list of all webhook event categories and associated payloads.

Event Categories

Account Balance Activity

CategoryPayload TypeDescriptionTrigger
MURAL_ACCOUNT_BALANCE_ACTIVITYaccount_creditedFunds received in a Mural accountIncoming blockchain transaction confirmed
MURAL_ACCOUNT_BALANCE_ACTIVITYaccount_debitedFunds sent from a Mural accountOutgoing blockchain transaction confirmed

Account Credited

interface AccountCreditedPayload {
  type: 'account_credited';
  accountId: string;
  organizationId: string;
  transactionId: string; // this is the id that will appear in the Transactions API
  accountWalletAddress: string;
  tokenAmount: {
    blockchain: string;
    tokenAmount: number;
    tokenSymbol: string;
    tokenContractAddress: string;
  };
  transactionDetails: {
    blockchain: string;
    transactionDate: string; // ISO 8601 datetime
    transactionHash: string;
    sourceWalletAddress: string;
    destinationWalletAddress: string;
  };
}

Account Debited

interface AccountDebitedPayload {
  type: 'account_debited';
  accountId: string;
  organizationId: string;
  transactionId: string; // this is the id that will appear in the Transactions API
  accountWalletAddress: string;
  tokenAmount: {
    blockchain: string;
    tokenAmount: number;
    tokenSymbol: string;
    tokenContractAddress: string;
  };
  transactionDetails: {
    blockchain: string;
    transactionDate: string; // ISO 8601 datetime
    transactionHash: string;
    sourceWalletAddress: string;
    destinationWalletAddress: string;
  };
}

Payout Request

CategoryPayload TypeDescriptionTrigger
PAYOUT_REQUESTpayout_request_status_changedPayout request status has changedPayout request transitions to a new status
PAYOUT_REQUESTpayout_status_changedIndividual payout status has changedFiat or blockchain payout transitions to a new status

Payout Request Status Changed Payload

This event is triggered when the overall payout request status changes. A payout request may contain multiple individual payouts (recipients).

interface PayoutRequestStatusChangedPayload {
  type: 'payout_request_status_changed';
  organizationId: string;
  payoutRequestId: string;
  statusChangeDetails: {
    previousStatus: PayoutRequestStatus;
    currentStatus: PayoutRequestStatus;
  };
}

type PayoutRequestStatus =
  | { type: 'awaitingExecution' } // Payout request created, awaiting blockchain execution
  | { type: 'pending' } // Blockchain transaction submitted, payouts in progress
  | { type: 'executed' } // All payouts completed successfully
  | { type: 'failed' } // One or more payouts failed
  | { type: 'canceled' }; // Payout request was canceled

Example Payload

{
  "type": "payout_request_status_changed",
  "organizationId": "e77ee023-fa9a-4d42-9be9-74f3a2bb2042",
  "payoutRequestId": "a3d9f1b2-7c4e-4a6f-9b1d-2e8c5f7a9d30",
  "statusChangeDetails": {
    "previousStatus": { "type": "awaitingExecution" },
    "currentStatus": { "type": "pending" }
  }
}

Payout Status Changed Payload

This event is triggered when an individual payout's status changes. The statusChangeDetails object contains payout type and status information, where type indicates whether this is a fiat (bank transfer) or blockchain payout.

interface PayoutStatusChangedPayload {
  type: 'payout_status_changed';
  organizationId: string;
  payoutRequestId: string;
  payoutId: string;
  statusChangeDetails: FiatPayoutStatusChangeDetails | BlockchainPayoutStatusChangeDetails;
}

// Fiat payout status change details (bank transfers)
interface FiatPayoutStatusChangeDetails {
  type: 'fiat';
  previousStatus: FiatPayoutStatus;
  currentStatus: FiatPayoutStatus;
}

// Blockchain payout status change details
interface BlockchainPayoutStatusChangeDetails {
  type: 'blockchain';
  previousStatus: BlockchainPayoutStatus;
  currentStatus: BlockchainPayoutStatus;
}

// Fiat payout statuses - objects with type field
type FiatPayoutStatus =
  | { type: 'created' } // Payout created, awaiting processing
  | { type: 'pending' } // Payout submitted to provider
  | { type: 'onHold' } // Payout on hold (compliance review)
  | { type: 'completed' } // Funds delivered successfully
  | { type: 'failed' } // Payout failed
  | { type: 'canceled' } // Payout was canceled
  | { type: 'refundInProgress' } // Refund is being processed
  | { type: 'refunded' }; // Funds refunded to source account

// Blockchain payout statuses - objects with type field
type BlockchainPayoutStatus =
  | { type: 'awaitingExecution' } // Awaiting blockchain transaction
  | { type: 'pending' } // Transaction submitted to blockchain
  | { type: 'executed' } // Transaction confirmed on blockchain
  | { type: 'failed' } // Transaction failed
  | { type: 'canceled' }; // Payout was canceled

Example Fiat Payout Payload

{
  "type": "payout_status_changed",
  "organizationId": "e77ee023-fa9a-4d42-9be9-74f3a2bb2042",
  "payoutRequestId": "a3d9f1b2-7c4e-4a6f-9b1d-2e8c5f7a9d30",
  "payoutId": "b4e8c2d1-5f6a-4b7c-8d9e-1a2b3c4d5e6f",
  "statusChangeDetails": {
    "type": "fiat",
    "previousStatus": { "type": "created" },
    "currentStatus": { "type": "pending" }
  }
}

Example Blockchain Payout Payload

{
  "type": "payout_status_changed",
  "organizationId": "e77ee023-fa9a-4d42-9be9-74f3a2bb2042",
  "payoutRequestId": "a3d9f1b2-7c4e-4a6f-9b1d-2e8c5f7a9d30",
  "payoutId": "c5f9d3e2-6a7b-4c8d-9e1f-2b3c4d5e6f7a",
  "statusChangeDetails": {
    "type": "blockchain",
    "previousStatus": { "type": "pending" },
    "currentStatus": { "type": "executed" }
  }
}

Payin

CategoryPayload TypeDescriptionTrigger
PAYINpayin_status_changedPayin status has changedPayin is created, or transitions to a new status

Payin Status Changed Payload

This event is triggered when a payin is created and whenever its status subsequently changes. The previousStatus field is null only on the initial creation event; for all subsequent transitions, it holds the prior status.

interface PayinStatusChangedPayload {
  type: 'payin_status_changed';
  organizationId: string;
  payinId: string;
  statusChangeDetails: {
    previousStatus: PayinStatus | null; // null on initial creation
    currentStatus: PayinStatus;
  };
}

type PayinStatus =
  | { type: 'created' }
  | { type: 'pending' }
  | { type: 'on-hold' }
  | { type: 'completed' }
  | { type: 'failed' }
  | { type: 'canceled' }
  | { type: 'refunded' };

Fetch full payin detail (including timestamps and the reason on failure) from the get Payin endpoint.

Example Creation Payload

{
  "type": "payin_status_changed",
  "organizationId": "e77ee023-fa9a-4d42-9be9-74f3a2bb2042",
  "payinId": "d192b782-647c-4011-8184-ab9c1e1b5b3d",
  "statusChangeDetails": {
    "previousStatus": null,
    "currentStatus": { "type": "created" }
  }
}

Example Transition Payload

{
  "type": "payin_status_changed",
  "organizationId": "e77ee023-fa9a-4d42-9be9-74f3a2bb2042",
  "payinId": "d192b782-647c-4011-8184-ab9c1e1b5b3d",
  "statusChangeDetails": {
    "previousStatus": { "type": "pending" },
    "currentStatus": { "type": "completed" }
  }
}

Business Verification

CategoryPayload TypeDescriptionTrigger
BUSINESS_VERIFICATION_STATUSbusiness_verification_status_changedBusiness verification status has been updatedKYC/KYB review completed or status change occurred

Verification Status Changed Payload

interface BusinessVerificationStatusChangedPayload {
  type: 'business_verification_status_changed';
  organizationId: string;
  previousStatus: MuralKycVerificationStatus;
  currentStatus: MuralKycVerificationStatus;
  verificationId: string;
  updatedAt: string; // ISO 8601 datetime
}

// Status objects returned based on verification state
type MuralKycVerificationStatus =
  | { type: 'inactive' }
  | { type: 'pending' }
  | { type: 'approved'; approvedAt: string }
  | { type: 'errored'; details: string; erroredAt: string }
  | { type: 'rejected'; reason: string; rejectedAt: string };

Example Status Objects

Inactive Status:

{ "type": "inactive" }

Pending Status:

{ "type": "pending" }

Approved Status:

{
  "type": "approved",
  "approvedAt": "2024-03-21T10:30:00Z"
}

Errored Status:

{
  "type": "errored",
  "details": "Verification encountered an error",
  "erroredAt": "2024-03-21T10:30:00Z"
}

Rejected Status:

{
  "type": "rejected",
  "reason": "Verification was rejected",
  "rejectedAt": "2024-03-21T10:30:00Z"
}

Organization Terms of Service

CategoryPayload TypeDescriptionTrigger
ORGANIZATION_TOStos_acceptedOrganization accepted Terms of ServiceInitial TOS acceptance by organization

TOS Accepted Payload

interface OrganizationTosAcceptedPayload {
  type: 'tos_accepted';
  organizationId: string;
  signedAt: string; // ISO 8601 datetime
  source: 'ui' | 'api' | 'test';
}

Source Field Values

  • ui: TOS was accepted through the Mural web application by an authenticated user
  • api: TOS was accepted via the public REST API endpoint
  • test: TOS event was triggered as a test webhook

Example Payload

{
  "type": "tos_accepted",
  "organizationId": "e77ee023-fa9a-4d42-9be9-74f3a2bb2042",
  "signedAt": "2024-03-21T10:30:00Z",
  "source": "ui"
}

Note: This event is only sent on initial TOS acceptance. Re-signing or updating agreements will not trigger additional webhook events.


Compliance Review

CategoryPayload TypeDescriptionTrigger
COMPLIANCE_REVIEWcompliance_review_status_changedCompliance review status has changedCompliance review transitions to a new status
COMPLIANCE_REVIEWattestation_validation_status_changedAttestation validation status has changedAn attestation's asynchronous validation finishes

Compliance Review Status Changed Payload

This event is triggered whenever a compliance review (individual or business) transitions to a new status — for example when a draft is submitted, when a submitted review moves into review, or when a review reaches a terminal approved, rejected, or error state.

The organizationId identifies the organization that owns the compliance review; for organizations with a parent/child structure this may be a parent or child organization. Both previousStatus and currentStatus use the same shape as the review status returned by the get Compliance Review endpoint.

interface ComplianceReviewStatusChangedPayload {
  type: 'compliance_review_status_changed';
  organizationId: string;
  complianceReviewId: string;
  previousStatus: ComplianceReviewStatus;
  currentStatus: ComplianceReviewStatus;
  updatedAt: string; // ISO 8601 datetime
}

// Each status has a type; some statuses carry extra fields
type ComplianceReviewStatus =
  | { type: 'draft' } // Review created, awaiting attestation uploads
  | { type: 'submitted'; submittedAt: string } // Submitted; submission validations will begin shortly
  | { type: 'validatingSubmission' } // Submission validations are currently running
  | { type: 'submissionValidationsFailed' } // One or more submission validations failed
  | { type: 'inReview'; submittedAt: string } // Currently being evaluated
  | { type: 'additionalInformationRequested'; rfiId: string } // More information required; rfiId identifies the outstanding RFI
  | { type: 'approved'; submittedAt: string; approvedAt: string } // Approved; requested KYC tier granted
  | {
      type: 'rejected';
      submittedAt: string;
      rejectedAt: string;
      rejectionDescription: string; // Reason the review was rejected
    }
  | {
      type: 'error';
      submittedAt: string;
      errorDescription: string; // Description of the processing error
    };

Status Field Notes

  • submittedAt is present on submitted, inReview, approved, rejected, and error. It is absent on draft, validatingSubmission, and submissionValidationsFailed.
  • approvedAt / rejectedAt are populated only once the review reaches the corresponding terminal state.
  • rejectionDescription is populated only on the rejected status.
  • errorDescription is populated only on the error status.
  • rfiId is populated only on the additionalInformationRequested status. It identifies the RFI to answer; see RFI.

Fetch full compliance review detail (tier, type, attestations, and additional metadata) from the get Compliance Review endpoint.

Example Submission Payload

{
  "type": "compliance_review_status_changed",
  "organizationId": "org_abc123",
  "complianceReviewId": "review_xyz789",
  "previousStatus": { "type": "draft" },
  "currentStatus": {
    "type": "submitted",
    "submittedAt": "2026-02-24T12:00:00.000Z"
  },
  "updatedAt": "2026-02-24T12:00:00.000Z"
}

Example Approval Payload

{
  "type": "compliance_review_status_changed",
  "organizationId": "org_abc123",
  "complianceReviewId": "review_xyz789",
  "previousStatus": {
    "type": "inReview",
    "submittedAt": "2026-02-24T12:00:00.000Z"
  },
  "currentStatus": {
    "type": "approved",
    "submittedAt": "2026-02-24T12:00:00.000Z",
    "approvedAt": "2026-02-25T10:30:00.000Z"
  },
  "updatedAt": "2026-02-25T10:30:00.000Z"
}

Example Rejection Payload

{
  "type": "compliance_review_status_changed",
  "organizationId": "org_abc123",
  "complianceReviewId": "review_def456",
  "previousStatus": {
    "type": "inReview",
    "submittedAt": "2026-02-24T12:00:00.000Z"
  },
  "currentStatus": {
    "type": "rejected",
    "submittedAt": "2026-02-24T12:00:00.000Z",
    "rejectedAt": "2026-02-25T10:30:00.000Z",
    "rejectionDescription": "Provided information does not meet KYC requirements."
  },
  "updatedAt": "2026-02-25T10:30:00.000Z"
}

Example Error Payload

{
  "type": "compliance_review_status_changed",
  "organizationId": "org_abc123",
  "complianceReviewId": "review_def456",
  "previousStatus": {
    "type": "inReview",
    "submittedAt": "2026-02-24T12:00:00.000Z"
  },
  "currentStatus": {
    "type": "error",
    "submittedAt": "2026-02-24T12:00:00.000Z",
    "errorDescription": "Verification provider returned an unexpected error."
  },
  "updatedAt": "2026-02-25T10:30:00.000Z"
}

Attestation Validation Status Changed Payload

Some attestations submitted to a compliance review, such as identity documents and addresses, are validated asynchronously. This event tells you the moment a specific attestation finishes, so you do not have to poll the review while validations run. It is delivered to webhooks subscribed to the COMPLIANCE_REVIEW category. It is not sent for associated-person attestations; poll the associated person for their validationStatus.

interface AttestationValidationStatusChangedPayload {
  type: 'attestation_validation_status_changed';
  organizationId: string;
  complianceReviewId: string;
  attestationId: string;
  attestationType: AttestationType; // e.g. 'individualIdentityDocument'
  statusChangeDetails: {
    previousStatus: { type: 'pending' };
    currentStatus: AttestationResolvedValidationStatus;
  };
  updatedAt: string; // ISO 8601 datetime
}

type AttestationResolvedValidationStatus =
  | { type: 'approved' } // The attestation passed every validation
  | { type: 'failed'; failures: AttestationValidationFailure[] };

currentStatus carries the same approved and failed shapes the attestation has in the get Compliance Review endpoint response, so a failed status arrives with its failures already populated — ordered most-actionable first. previousStatus is always pending: an attestation leaves pending once and never returns to it.

An attestation that fails validation does not by itself fail the review. Correct the attestation and resubmit it; each resubmission that resolves emits another event.

Example Approval Payload

{
  "type": "attestation_validation_status_changed",
  "organizationId": "org_abc123",
  "complianceReviewId": "review_xyz789",
  "attestationId": "attestation_def456",
  "attestationType": "individualIdentityDocument",
  "statusChangeDetails": {
    "previousStatus": { "type": "pending" },
    "currentStatus": { "type": "approved" }
  },
  "updatedAt": "2024-03-21T10:30:00.000Z"
}

Example Failure Payload

{
  "type": "attestation_validation_status_changed",
  "organizationId": "org_abc123",
  "complianceReviewId": "review_xyz789",
  "attestationId": "attestation_def456",
  "attestationType": "individualIdentityDocument",
  "statusChangeDetails": {
    "previousStatus": { "type": "pending" },
    "currentStatus": {
      "type": "failed",
      "failures": [
        {
          "severity": "WARNING",
          "failureInfo": {
            "type": "general",
            "failureCode": "POOR_QUALITY"
          }
        }
      ]
    }
  },
  "updatedAt": "2024-03-21T10:30:00.000Z"
}

RFI

CategoryPayload TypeDescriptionTrigger
RFIrfi_createdA request for information was raisedA reviewer raises an RFI
RFIrfi_status_changedRFI status has changedThe RFI transitions to a new status

A reviewer raises a request for information (RFI) when they need more from you before a compliance review can continue. See Requests for Information (RFIs) for the lifecycle and Responding to RFIs Programmatically for a full walkthrough.

Both events carry an origin that names what the RFI was raised on. Read its type to know which id it carries. Every RFI is currently raised on a compliance review.

type RfiEventOrigin = {
  type: 'complianceReview';
  complianceReviewId: string;
};

type RfiStatusType =
  | 'outstanding' // The reviewer is waiting for a response
  | 'validatingSubmission' // Your submission is being validated
  | 'submissionValidationsFailed' // One or more submission validations failed; correct and submit again
  | 'submitted' // Your response was submitted; follow the compliance review for the outcome
  | 'cancelled'; // The request was cancelled; read the RFI for the reason

RFI Created Payload

This event is triggered when a reviewer raises an RFI. The compliance review it was raised on moves to additionalInformationRequested at the same time, and you also receive a compliance_review_status_changed event. Read the RFI from the get RFI endpoint to see what is requested.

interface RfiCreatedPayload {
  type: 'rfi_created';
  organizationId: string;
  rfiId: string;
  origin: RfiEventOrigin;
  createdAt: string; // ISO 8601 datetime the reviewer made the request
}

Example Payload

{
  "type": "rfi_created",
  "organizationId": "0c1c0f50-7a89-4e3a-9b1a-1d4ce4d6f001",
  "rfiId": "3f1a9c22-55b8-4e01-9a7d-2c4f6b8e1d03",
  "origin": {
    "type": "complianceReview",
    "complianceReviewId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  },
  "createdAt": "2026-02-25T09:15:00.000Z"
}

RFI Status Changed Payload

This event is triggered whenever an RFI moves to a different status: for example, when your submission starts validating, fails validation, is submitted, or the request is cancelled. Each step is its own event, so a failed resubmission sends two: one into validatingSubmission, and one back to submissionValidationsFailed.

The payload carries only the status types. Read the RFI from the get RFI endpoint to see the detail behind a status, such as the failures of a rejected submission.

interface RfiStatusChangedPayload {
  type: 'rfi_status_changed';
  organizationId: string;
  rfiId: string;
  origin: RfiEventOrigin;
  statusChangeDetails: {
    previousStatus: RfiStatusType;
    currentStatus: RfiStatusType;
  };
  updatedAt: string; // ISO 8601 datetime
}

Example Payload

{
  "type": "rfi_status_changed",
  "organizationId": "0c1c0f50-7a89-4e3a-9b1a-1d4ce4d6f001",
  "rfiId": "3f1a9c22-55b8-4e01-9a7d-2c4f6b8e1d03",
  "origin": {
    "type": "complianceReview",
    "complianceReviewId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
  },
  "statusChangeDetails": {
    "previousStatus": "validatingSubmission",
    "currentStatus": "submitted"
  },
  "updatedAt": "2026-02-26T14:05:00.000Z"
}

Did this page help you?