> ## Documentation Index
> Fetch the complete documentation index at: https://docs.legitmark.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Event Types

> Detailed payload schemas and field references for every Legitmark webhook event.

Legitmark sends three event types. Every webhook destination receives all events automatically.

## state\_change

Sent when a service request transitions to a new state. This is the primary event for tracking authentication progress and results.

### Payload

```json theme={null}
{
  "event_type": "state_change",
  "sr_uuid": "b16c763b-1723-455d-ba29-164418044886",
  "reference_id": "YOUR-INTERNAL-ITEM-ID",
  "state": {
    "primary": "COMPLETE",
    "supplement": "APPROVED"
  },
  "timestamp": "2026-02-10T12:00:00.000Z"
}
```

### Fields

<ResponseField name="event_type" type="string" required>
  Always `"state_change"`.
</ResponseField>

<ResponseField name="sr_uuid" type="string" required>
  Service request UUID.
</ResponseField>

<ResponseField name="reference_id" type="string | null">
  Your internal item ID — the `external_id` you set when creating the service request.
</ResponseField>

<ResponseField name="state" type="object" required>
  <Expandable title="state properties">
    <ResponseField name="primary" type="string" required>
      Primary state of the service request.
    </ResponseField>

    <ResponseField name="supplement" type="string | null">
      Supplementary state detail providing additional context.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="timestamp" type="string" required>
  ISO 8601 timestamp of when the state change occurred.
</ResponseField>

### State Transitions

The table below shows the state transitions you may receive:

| `state.primary` | `state.supplement` | Meaning                                                                      |
| --------------- | ------------------ | ---------------------------------------------------------------------------- |
| `QC`            | `PENDING`          | Photos submitted, under quality review                                       |
| `QC`            | `REJECTED`         | Photos failed quality review — a `media_rejected` event follows with details |
| `QC`            | `APPROVED`         | Photos passed QC, authentication starting                                    |
| `UNDERWAY`      | `ASSIGNED`         | Authenticator assigned                                                       |
| `COMPLETE`      | `APPROVED`         | Item is authentic                                                            |
| `COMPLETE`      | `REJECTED`         | Item is not authentic                                                        |

<Tip>
  The most important transition is `COMPLETE` + `APPROVED` or `COMPLETE` + `REJECTED` — this is the **final authentication result**. See [Service Request States](/partner/service-request-states) for the full state machine.

  Using the TypeScript SDK (`v0.2.0+`), you can check these with `isAuthentic(event)` and `isCounterfeit(event)` instead of comparing strings manually. See [Handling Webhooks](/webhook-reference/handling).
</Tip>

***

## media\_rejected

Sent when uploaded images fail quality control. Each rejected image includes the side name and rejection reason so you can prompt the user to re-upload specific photos.

### Payload

```json theme={null}
{
  "event_type": "media_rejected",
  "sr_uuid": "b16c763b-1723-455d-ba29-164418044886",
  "reference_id": "YOUR-INTERNAL-ITEM-ID",
  "sides": [
    {
      "side": "Front",
      "reason": "Image is Blurry",
      "message": "The submitted image is too blurry. Please capture a clearer photo, ensuring minimal motion for better accuracy."
    },
    {
      "side": "Interior",
      "reason": "Insufficient Lighting",
      "message": "The lighting in the submitted image is insufficient. Please take a new photo with better lighting for improved visibility."
    }
  ],
  "timestamp": "2026-02-10T12:15:00.000Z"
}
```

### Fields

<ResponseField name="event_type" type="string" required>
  Always `"media_rejected"`.
</ResponseField>

<ResponseField name="sr_uuid" type="string" required>
  Service request UUID.
</ResponseField>

<ResponseField name="reference_id" type="string | null">
  Your internal item ID.
</ResponseField>

<ResponseField name="sides" type="array" required>
  Array of rejected images with reasons.

  <Expandable title="sides[] properties">
    <ResponseField name="side" type="string" required>
      Name of the rejected side (e.g. `"Front"`, `"Label"`, `"Back"`).
    </ResponseField>

    <ResponseField name="reason" type="string" required>
      Rejection reason. These are human-readable strings from the Legitmark reasons database (e.g. `"Image is Blurry"`, `"Insufficient Lighting"`). Values may change as new reasons are added.
    </ResponseField>

    <ResponseField name="message" type="string">
      Extended description with guidance for the user on how to fix the issue.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="timestamp" type="string" required>
  ISO 8601 timestamp.
</ResponseField>

<Warning>
  When you receive a `media_rejected` event, the user needs to re-upload the specific rejected sides before the service request can proceed. Prompt them with the `side` name and `message` for the best experience.
</Warning>

<Tip>
  Using the TypeScript SDK, `needsResubmission(event)` returns `true` for this event type. See [Handling Webhooks](/webhook-reference/handling).
</Tip>

***

## invalidate\_sr

Sent when a service request is cancelled.

### Payload

```json theme={null}
{
  "event_type": "invalidate_sr",
  "sr_uuid": "b16c763b-1723-455d-ba29-164418044886",
  "reference_id": "YOUR-INTERNAL-ITEM-ID",
  "invalidation_reason": {
    "code": "CANCELLED",
    "message": "Unsupported Product or SKU. The product or SKU provided isn't supported under our current service guidelines."
  },
  "timestamp": "2026-02-10T14:30:00.000Z"
}
```

### Fields

<ResponseField name="event_type" type="string" required>
  Always `"invalidate_sr"`.
</ResponseField>

<ResponseField name="sr_uuid" type="string" required>
  Service request UUID.
</ResponseField>

<ResponseField name="reference_id" type="string | null">
  Your internal item ID.
</ResponseField>

<ResponseField name="invalidation_reason" type="object" required>
  <Expandable title="invalidation_reason properties">
    <ResponseField name="code" type="string" required>
      Reason code. Currently always `"CANCELLED"`. Additional codes may be introduced in the future.
    </ResponseField>

    <ResponseField name="message" type="string" required>
      The specific cancellation reason. Common values include:

      * `"Unsupported Product or SKU. The product or SKU provided isn't supported under our current service guidelines."`
      * `"Unsupported Channel"`
      * `"Service Request Inactivity"`
      * `"Duplicate Service Request"`
      * `"Insufficient Product Information"`
      * `"Customer Requested Cancellation"`
      * `"Poor Image Quality or Unusable Media"`

      Custom free-text reasons may also appear.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="timestamp" type="string" required>
  ISO 8601 timestamp.
</ResponseField>
