# About webhooks for Billing Management

Billing Management can send webhook notifications to your system to keep you updated on the status of your invoices. These webhooks are sent out at key moments in the invoice lifecycle, allowing you to build powerful integrations and automate your workflows.

This document guides you through the steps to register your organization's webhook directly in the Billing Management application.

## Who uses Billing Management webhooks

Billing Management webhooks are available to organizations using Shipium's Billing Management product. This includes:

- **3PL organizations.** Third-party logistics providers who manage shipping for multiple tenants and need to automate invoice delivery to their customers
- **Enterprise organizations.** Large shippers who want automated notification when invoices are ready for processing in their internal systems

Your use case determines how you configure webhooks for your organization. See the registration options below and the steps that follow to get started.

## Webhook registration options

Your organization can register webhooks in the way that best fits your architecture:

**Single endpoint for all events.** Register one webhook endpoint to receive all invoice event types (`invoice_created`, `invoice_finalized`, `invoice_voided`). Your application then routes events internally based on the `eventType` field in the metadata.

**Separate endpoints per event type.** Register different webhook endpoints for each event type if you need events routed to different systems (for example, `invoice_finalized` events to your accounting system and `invoice_created` events to a review dashboard).

**Tenant-specific routing (3PL organizations).** If you manage multiple tenants, you can either:

- register one webhook to receive notifications for all tenants and route them internally using the `shipiumTenantId` field, or
- register separate webhooks for specific tenants with unique API endpoints.

Enterprise organizations typically register one webhook per internal system that needs to receive invoice notifications (for example, your accounting system or ERP).

Once you've registered your organization's webhook, you can disable, delete, and enable it in the Billing Management application. You also can view a summary of all your registered webhooks within the application.

# Steps to register your webhook

To receive webhook notifications, you will need to register a webhook endpoint in the [Billing Management application](https://billing-management.shipium.com/). Access to the Billing Management application requires the feature to be enabled for your account. Contact your Shipium representative if you do not have access.

Once in the Billing Management application, follow these steps to register your webhook:

1. In the left navigation, select **Billing Webhooks**.

2. Select the **Add Webhook** button. Any existing webhooks your organization has previously registered will appear on this screen.

3. Complete the **Settings** fields:
   1. **Name.** Provide a descriptive name for your webhook.
   2. **Payload URL.** Enter the HTTPS URL where you want to receive webhook notifications. HTTP endpoints are not supported.
   3. **Status.** Shipium automatically sets new webhooks to inactive by default. This allows you to test your webhook before activating it.
   4. **Tenants.** This field only appears if your organization uses Shipium's **Tenant** feature. Select **All** to receive notifications for all tenants, or select specific tenants from the dropdown.
4. Optionally, add custom HTTP headers to your webhook. In the **Headers** section, select **Add Additional Header** and provide a key and value for each header you want to include. Headers are sent with every webhook request Shipium delivers to your endpoint.
   
   Custom headers are commonly used to add an `Authorization` header for HTTP Basic Authentication. For step-by-step instructions on generating and configuring custom headers, see [Configure Custom HTTP Headers for Webhooks](https://docs.shipium.com/docs/configure-custom-http-headers-for-webhooks).

5. Select the invoice **Event Type** you want to receive notifications for: `invoice_created`, `invoice_finalized`, `invoice_voided`, or all three.

6. Send a test payload to your endpoint to confirm successful delivery before activating your webhook.

## Auto-cut invoices

Billing Management supports automatic invoice finalization through the auto-cut feature. When auto-cut is enabled for a billing schedule, invoices generated by that schedule automatically transition to `APPROVED` status, which triggers the `invoice_finalized` webhook.

Auto-cut can be configured at the schedule level or overridden for specific tenants. Contact your Shipium representative to enable auto-cut for your billing schedules.

# Invoice event types for your webhook

The following table includes the event metadata event types that can be returned for an invoice event notification. You can register a single webhook endpoint to receive all event types, or register separate endpoints for specific events based on your integration needs.

| Event type for webhook | Trigger | Purpose |
| --- | --- | --- |
| `invoice_created` | Invoice generated in DRAFT status | Notification that a new invoice is ready for review |
| `invoice_finalized` | Invoice approved and finalized | Data delivery with presigned URL for CSV download |
| `invoice_voided` | Invoice canceled/voided | Notification that an invoice has been canceled |

# Invoice lifecycle

The following table shows when each webhook event is triggered during the invoice lifecycle.

| Invoice status | Transitions to | Webhook event trigger |
| --- | --- | --- |
| `DRAFT` | `PENDING_APPROVAL` or `CANCELED` | `invoice_created` |
| `PENDING_APPROVAL` | `APPROVED` or `CANCELED` | (no event) |
| `APPROVED` | `CANCELED` | `invoice_finalized` |
| `CANCELED` | — | `invoice_voided` |

# Webhook message structure

All billing webhooks use a standard envelope structure with metadata and payload.

JSON

```json
{
  "metadata": { ... },
  "payload": { ... }
}
```

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `metadata` | _Object_ | Yes | Event metadata for routing and idempotency |
| `payload` | _Object_ | Yes | Event-specific data (varies by event type) |

## Event metadata

The metadata object is identical across all event types.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `eventId` | _String (UUID)_ | Yes | Unique identifier for idempotency handling. Use this to prevent duplicate processing. |
| `eventTimestamp` | _String (ISO 8601)_ | Yes | UTC timestamp when the event was generated |
| `eventType` | _String_ | Yes | One of: `invoice_created`, `invoice_finalized`, `invoice_voided` |
| `partnerId` | _String (UUID)_ | Yes | Partner identifier for routing |
| `payloadSchemaVersion` | _String_ | Yes | Schema version (currently v1) |
| `testEvent` | _Boolean_ | Yes | `True` if this is a test event (should not be processed as real data) |

## Invoice payload

The `payload` object contains invoice details. The following fields are included in all event types.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `shipiumInvoiceId` | _String (UUID)_ | Yes | Unique Shipium invoice identifier. Use this for API calls. |
| `partnerInvoiceId` | _String_ | No | Partner's internal invoice reference (if assigned) |
| `shipiumTenantId` | _String (UUID)_ | No | Shipium tenant identifier. `null` for partner-level invoices. |
| `partnerTenantId` | _String_ | No | Partner's external tenant identifier |
| `invoiceNumber` | _String_ | Yes | Human-readable invoice number for display |
| `invoiceStatus` | _String_ | Yes | Current status: `draft`, `finalized`, or `voided` |
| `invoiceStartDate` | _String (ISO 8601)_ | Yes | Start of billing period |
| `invoiceEndDate` | _String (ISO 8601)_ | Yes | End of billing period |
| `invoiceDate` | _String (ISO 8601)_ | Yes | Date invoice was generated |
| `invoiceDueDate` | _String (ISO 8601)_ | Yes | Payment due date (typically invoice date + 15 days) |
| `currencyCode` | _String_ | Yes | ISO 4217 currency code (e.g., `USD`) |
| `invoiceTotalAmount` | _Number_ | Yes | Total invoice amount in the specified currency |
| `totalTransactionCount` | _Integer_ | Yes | Number of carrier transactions (shipments) in this invoice |

## Invoice finalized payload fields

The `invoice_finalized` event includes additional fields for downloading the invoice CSV file.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `presignedUrl` | _String (URL)_ | Yes | Time-limited URL to download the invoice CSV from S3 |
| `presignedUrlExpiresAt` | _String (ISO 8601)_ | Yes | Timestamp when the presigned URL expires |
| `fileHashSha256` | _String_ | Yes | SHA-256 hash of the CSV file for integrity verification |
| `fileSizeBytes` | _Integer_ | Yes | Size of the CSV file in bytes |

## Downloading invoice data

When an invoice is finalized, Shipium exports the invoice data to a CSV file and provides a presigned S3 URL in the webhook payload. Use this URL to download the complete invoice data, including all carrier transactions.

## File characteristics:

- **Encoding.** UTF-8
- **Line ending.** CRLF (`\r\n`)
- **Quoting.** Fields containing commas, quotes, or newlines are quoted.
- **Header row.** Always included as the first row
- **Sorting.** Rows are not guaranteed to be in any particular order.

## Status mapping reference

The following table shows how internal invoice statuses map to webhook event types.

| Internal status | Webhook status | Event type |
| --- | --- | --- |
| `DRAFT` | `draft` | `invoice_created` |
| `PENDING_APPROVAL` | `pending` | (no event) |
| `APPROVED` | `finalized` | `invoice_finalized` |
| `CANCELED` | `voided` | `invoice_voided` |

## Troubleshooting

### Webhook not receiving events
- Confirm with your Shipium representative that your webhook is active.
- Check that your endpoint URL is correct and accessible from the internet.
- Ensure your endpoint responds within the 30-second timeout.
- Verify firewall rules allow incoming requests from Shipium's infrastructure.

### Duplicate events received
- Implement idempotency handling using the `eventId` field.
- Check if your endpoint is returning non-2xx responses, which triggers retries.

### Presigned URL expired
Use the `POST /api/v1/billing/invoices/{invoiceId}/regenerate-presigned-url` endpoint to generate a new URL. URLs expire 24 hours after generation.
