# Issuing real-time authentications

Learn how to set up an integration to respond to real-time Issuing authentication webhooks.

> #### Availability
> 
> Real-time authentication decisions are available only for eligible, supported Issuing integrations. Contact your Stripe account manager or [Stripe Support](https://support.stripe.com/) to confirm eligibility and request access.

Your authentications synchronous webhook is only used for authentications requests. All other notifications are sent to your regular webhook endpoint.

You can decide to proceed with an approval, decline, or cardholder challenge using the synchronous webhook.

You can configure your webhook endpoint in your [Dashboard settings](https://dashboard.stripe.com/account/issuing). When a user uses a card to make a purchase, Stripe creates an `issuing_authentication.decision` event and sends it to your configured endpoint for your approval.

## Authentication requests

Here is an example of what the `issuing_authentication.decision` synchronous webhook might contain:

```json
{
    "id": "iauthn_1CmMk2IyNTgGDVfzFKlCm0gU",
    "object": "issuing.authentication",
    "card": "{{CARD_ID}}",
    "cardholder": "{{CARDHOLDER_ID}}",
    "challenge": null,
    "merchant_amount": 50000,
    "merchant_currency": "usd",
    "status": "pending_challenge_decision",
    ...
}
```

## Respond to authentication requests 

You can respond to authentication requests by responding directly to the webhook event. Respond to the `issuing_authentication.decision` webhook event directly to either approve, decline or proceed with a challenge for an authentication after it’s received.

#### Webhook response

Our webhook accepts JSON responses with the following parameters (a`200` response status code indicates success):

**Header**

| field name | required or optional | description |
| --- | --- | --- |
| **Stripe-Version** | Required | Version in `YYYY-dd-mm` format. |
| **Content-Type** | Optional | The only content type accepted for Authentication webhook responses is `application/json`. |

**Body**

| field name | required or optional | type | description |
| --- | --- | --- | --- |
| **challenge** | Required | Boolean | To contain the `decision` field. |
| **decision** | Required | Enum | Determines how to proceed with the authentication. One of `challenge_cardholder`, `approve_without_challenge` or `decline_without_challenge`. |
| **metadata** | Optional | Set of [key-value pairs](https://docs.stripe.com/api/metadata.md) | This is useful for storing additional information about the object in a structured format. |

## Handle webhooks from Stripe

Learn how to properly respond to the webhook and how the webhook outcome reflects onto the authentication object.

## Read the event data

Stripe sends the event data in the request body. Each event is structured as an [Event object](https://docs.stripe.com/api/events.md) with a `type`, `id`, and related Stripe resource nested under `data`.

## Handle the event

As soon as you have the event object, check the [type](https://docs.stripe.com/api/events/types.md) and filter for `issuing_authentication.decision`. This is the webhook event that Stripe sends when [3DS](https://docs.stripe.com/issuing/3d-secure.md) is requested. Write your business logic here to make a decision for the incoming authentication. For example, you can challenge any authentication over a certain `merchant_amount`.

## Respond to the webhook

The `issuing_authentication.decision` webhook is synchronous, which enables you make a decision per authentication in real time. You can include your decision in the response body of the webhook request. To do so you must also specify the `Content-Type` header as `application/json`.

After Stripe processes the response, we fire a discrete `issuing_authentication.updated` event. In this, the top-level `challenge` in the request turns to `null`, and `status` becomes `pending_challenge_decisions`.

When you answer the request, the top-level `challenge.decision` reflects your challenge decision—either `approve_without_challenge`, `decline_without_challenge`, or `challenge_cardholder`. Accordingly, `status` changes to `approved`, `failed`, or `requires_api_approval`, and `challenge.decision_reason` gets filled with `webhook_response`

Here’s an example integration for a local endpoint at `http://localhost:4242/webhook`:

#### Ruby

```ruby
# Using Sinatra.
require 'sinatra'
require 'stripe'

set :port, 4242

# Don't put any keys in code. See https://docs.stripe.com/keys-best-practices.
# Find your keys at https://dashboard.stripe.com/apikeys.
client = Stripe::StripeClient.new('<<YOUR_SECRET_KEY>>')

# Replace with a real secret. You can find your endpoint's secret in your webhook settings.
webhook_secret = 'whsec_...'

post '/webhook' do
  payload = request.body.read
  sig_header = request.env['HTTP_STRIPE_SIGNATURE']

  event = nil

  # Verify webhook signature and extract the event.
  begin
    event = Stripe::Webhook.construct_event(
      payload, sig_header, webhook_secret
    )
  rescue JSON::ParserError => e
    # Invalid payload.
    status 400
    return
  rescue Stripe::SignatureVerificationError => e
    # Invalid signature.
    status 400
    return
  end

  if event['type'] == 'issuing_authentication.decision'
    auth = event['data']['object']
    # ... custom business logic

    status 200
    header 'Stripe-Version' => '2022-08-01', 'Content-Type' => 'application/json'
    data = { 'challenge' => {'decision' => 'challenge_cardholder'} }
    body data.to_json
  end
  # ...handle other cases
end
```

## Webhook timeouts and errors

### User timeout

If Stripe doesn’t receive your response within three seconds, the [authentication](https://docs.stripe.com/api/issuing/authentications.md) proceeds with `cardholder_challenge` and `webhook_timeout` events, and a `challenge.decision_reason`.

### Invalid response

When the response sent through the synchronous authentication webhook is invalid, Stripe populates `challenge.decision_reason` with `webhook_error`. The `challenge.decision_reason_message` field contains a more detailed description of the error.

### Stripe error

If Stripe can’t process your response successfully, the [authentication](https://docs.stripe.com/api/issuing/authentications.md) proceeds with a `cardholder_challenge`. In this case, Stripe sets `challenge.decision_reason` to `stripe_error`.
