Issuing real-time authorizations
Learn about real-time authorizations.
Using the synchronous webhook, you can approve or decline authorization requests in real time.
Your webhook endpoint can be configured in your settings. When a card is used to make a purchase, Stripe creates an issuing_authorization.request and sends it to your configured endpoint for your approval.
Get started with our interactive guide to real-time authorizations.
Responding to authorization requests
You can respond to authorization requests by responding directly to the webhook event.
Respond directly
Respond to the issuing_authorization.request webhook event directly to either approve or decline an authorization after it’s received.
Webhook response
Our webhook accepts JSON responses with the following parameters:
Status code: Return 200 to indicate success.
Header:
| field name | required or optional | description |
|---|---|---|
| Stripe-Version | required | See API Versioning for supported values. |
| Content-Type | optional | The only content type accepted for Authorization webhook responses is application/json. |
Body:
| field name | required or optional | type | description |
|---|---|---|---|
| approved | required | Boolean | Set true to approve an authorization and false to decline. |
| amount | optional | Integer | If the authorization’s pending_request.is_amount_controllable property is true, you can provide this value to control how much to hold for the authorization. It must be positive. |
| metadata | optional | Set of key-value pairs | This can be useful for storing additional information about the object in a structured format. |
| send_fraud_challenges Public preview | optional | Array of strings | You can send a fraud challenge for this authorization only through SMS. Leave it blank if you don’t want to send a challenge. |
server.rb
Select a language
Ruby
Python
PHP
Node.js
Java
Go
.NET
No results
# 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('sk_test_BQokikJOvBiI2HlWgH4olfQ2')
# 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_authorization.request'
auth = event['data']['object']
# ... custom business logic
status 200
header 'Stripe-Version' => '2025-03-31.basil', 'Content-Type' => 'application/json'
data = { 'approved' => true }
body data.to_json
end
# ...handle other cases
end
If Stripe doesn’t receive your approve or decline response within 2 seconds, the Authorization is automatically approved or declined based on your timeout settings, or Autopilot settings, if configured.
Note
If your Issuing balance has insufficient funds for the incoming authorization, the authorization will be denied and your webhook endpoint won’t receive the issuing_authorization.request event. To learn more about funding your Issuing balance, read here.
Authorization requests
When an authorization request is sent to your webhook, the amount requested is stored in pending_request.
{
"id": "iauth_1CmMk2IyNTgGDVfzFKlCm0gU",
"object": "issuing_authorization",
"approved": false,
"amount": 0,
"currency": "usd",
"status": "pending",
...
"pending_request": {
"amount": 400,
"currency": "usd",
"merchant_amount": 360,
"merchant_currency": "gbp"
}
}
The top-level amount in the request is set to 0 and approved is false. After you respond to the request, the top-level amount reflects the total amount approved or declined, the approved field is updated, and pending_request is set to null.
Testing webhooks locally
To test webhooks locally, you can use Stripe CLI. After you install it, you can forward events to your server:
Command Line
stripe listen --forward-to localhost:4242/webhook
Ready! Your webhook signing secret is '{{WEBHOOK_SIGNING_SECRET}}' (^C to quit)
In another terminal, you can then manually trigger issuing_authorization.request events from the CLI for more streamlined testing.
Command Line
stripe trigger issuing_authorization.request
Learn more about setting up webhooks.
Monitor your webhook responses
Monitor your webhook responses for timeouts and errors by subscribing to the issuing_authorization.created and issuing_authorization.updated webhook events. When the request_history.reason property is either webhook_error or webhook_timeout, Stripe isn’t receiving your responses. Don’t rely on approved to gauge integration health because Autopilot can approve on your behalf during an error condition. For example, investigate if an authorization has approved: true but request_history.reason: webhook_error. We surface detailed error messages in the request_history.reason_message field.
Autopilot Public preview
Autopilot provides fallback options that allow you to continue making real-time authorization decisions when your systems are down, don’t respond to an authorization request, or provide an invalid response.
Autopilot makes an authorization decision on your behalf based on a predefined set of rules. We create Authorization objects for transmission to reconcile Autopilot transactions. When Autopilot approves or declines an authorization, the request_history.reason property of the issuing_authorization.created webhook is either webhook_error or webhook_timeout:
- webhook _ error if you respond to the real-time authorization webhook with an invalid Stripe API version in the request headers , or if we can’t process your response.
- webhook _ timeout for all other failure modes.
To configure Autopilot, contact Stripe support, and monitor.
Stripe Autopilot Public preview
For users with their own dedicated Bank Identification Numbers (BIN), Stripe Autopilot can help make authorization decisions when the card network can’t reach Stripe.
When an authorization is approved or declined through Stripe Autopilot while Stripe is down, the request_history.reason property of the issuing_authorization.created webhook is network_fallback.
In some cases, Stripe declines an authorization but can’t communicate with the card network, and the network approves the authorization. When that happens, Stripe doesn’t hold the authorized funds, but they might still be captured.
To configure Stripe Autopilot, contact Stripe support, and monitor.
Fraud challenges through webhooks Public preview
Fraud challenges allow your cardholders to retry non-fraudulent transactions that would have otherwise been blocked. While fraud challenges are generally available, the ability to trigger them yourself through real-time webhook responses is in public preview.
To manage the rules that dictate when a fraud challenge is sent, adjust your response to the issuing_authorization.request webhook. You can trigger fraud challenges in scenarios where you detect spending that appears suspicious and want additional verification (for example, a cardholder using their card out of the country).
To do so, decline the issuing_authorization.request webhook and include the send_fraud_challenges field with the ["sms"] value.
Triggering fraud challenges through real-time webhook responses is currently limited to preview users. You must be an Issuing customer to join the preview. To request access to the preview, log in to your Stripe account and refresh the page. Contact Stripe for more information.
