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

# Authorization webhook

> Approve or decline an account's card authorizations from your own endpoint.

An authorization webhook is an endpoint of yours that Slash asks about every
card authorization on an account before answering the card network. Your
answer declines the authorization or lets it continue through Slash's own
checks, such as balance and limits.

> [!NOTE]
>
> Authorization webhooks are an Enterprise feature. Contact Slash support to
> turn them on.

## Setting up

Create the webhook `inactive` with
[`PUT /v2/accounts/{accountId}/authorization-webhook`](https://docs.slash.com/api-reference/authorization-webhook/set),
so you can store its `signingSecret` before any request arrives:

```bash theme={null}
curl -X PUT "https://api.slash.com/v2/accounts/$ACCOUNT_ID/authorization-webhook" \
  -H "X-API-Key: $SLASH_API_KEY" \
  -H "x-legal-entity: $SLASH_LEGAL_ENTITY_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "webhookUrl": "https://example.com/slash/authorize",
    "status": "inactive",
    "config": { "fallbackBehavior": "reject" }
  }'
```

Once your endpoint verifies requests, set `status` to `active` with
[`PATCH /v2/accounts/{accountId}/authorization-webhook`](https://docs.slash.com/api-reference/authorization-webhook/update).
The secret doesn't change when the webhook does.
[`GET /v2/accounts/{accountId}/authorization-webhook`](https://docs.slash.com/api-reference/authorization-webhook/retrieve)
returns the current configuration. A webhook set up with v1 is the same
webhook here.

## Requests

Slash sends each authorization to `webhookUrl` as a JSON `POST`:

```json theme={null}
{
  "event": { "type": "authorization.request", "id": "webhook_event_2rj4l7hmrrrcv" },
  "data": {
    "transaction": { "id": "385295285293505", "timestamp": "2026-09-19T18:04:11.000Z" },
    "card": { "id": "c_2rj4l7hmrrrcv", "name": "Ads – Meta" },
    "account": { "id": "sa_group_3k8m2p9qwexyz", "name": "Operating" },
    "virtualAccount": { "id": "subaccount_2rj4l7hmrrrcv", "name": "Marketing" },
    "merchant": {
      "name": "UBER *TRIP",
      "categoryCode": "4121",
      "location": { "city": "SAN FRANCISCO", "state": "CA", "country": "US", "zip": "94103" }
    },
    "amount": { "amountCents": 4250 },
    "currencyConversion": {
      "conversionRate": 1.0842,
      "originalAmountCents": 3920,
      "originalCurrencyCode": "EUR"
    }
  }
}
```

`transaction.id` is the `providerAuthorizationId` of the resulting card
transaction. `currencyConversion` is only there when the merchant charged in
another currency; `amount` is always in USD. v1 webhooks receive the same
body, so its amounts are integer cents rather than v2's `Money`.

### Verifying requests

Every request carries `x-webhook-id`, `x-webhook-timestamp` (Unix seconds)
and `x-webhook-signature`. Reject a request whose timestamp is more than two
minutes old. The signature is `v1=` followed by the base64 HMAC-SHA256 of
`{x-webhook-id}.{x-webhook-timestamp}.{raw body}`, keyed with `signingSecret`
base64-decoded after its `whsec_` prefix:

```javascript theme={null}
import crypto from 'node:crypto';

function verify({ id, timestamp, signature, rawBody, signingSecret }) {
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 120) return false;
  const key = Buffer.from(signingSecret.replace('whsec_', ''), 'base64');
  const expected = crypto
    .createHmac('sha256', key)
    .update(`${id}.${timestamp}.${rawBody}`)
    .digest();
  const received = Buffer.from(signature.replace('v1=', ''), 'base64');
  return (
    received.length === expected.length &&
    crypto.timingSafeEqual(received, expected)
  );
}
```

## Answering

Respond within `timeoutDurationMs` with a `2xx` status and a JSON body:

```json theme={null}
{ "approved": false, "reason": "No open purchase order" }
```

`approved: true` lets the authorization continue through Slash's checks, and
`false` declines it. `reason` is optional and shows on the transaction in the
Slash dashboard.

## Fallback

`config` decides an authorization your endpoint doesn't answer: it times out,
returns a non-`2xx` status or a body without a boolean `approved`, or can't
be reached, including when Slash can't consult webhooks at all. `reject`
declines it. `default` lets it continue, or with `when`, only when it matches
and declines the rest:

```json theme={null}
{
  "fallbackBehavior": "default",
  "when": {
    "operator": "and",
    "left": { "operator": "lte", "field": "amountCents", "value": 2000 },
    "right": { "operator": "in", "field": "merchantCategoryCode", "values": ["7311"] }
  }
}
```


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.