Skip to content
Create account
or
Sign in
The Stripe Docs logo
/
Ask AI
Create account
Sign in
Get started
Payments
Finance automation
Platforms and marketplaces
Money management
Developer tools
Get started
Payments
Finance automation
Get started
Payments
Finance automation
Platforms and marketplaces
Money management
Overview
Start an integration
Products
Global Payouts
Capital
Issuing cards
    Overview
    How Issuing works
    Global availability
    Manage fraud
    Cards
    Choose your card type
    Virtual cards
    Issue virtual cards
    Physical cards
    Manage cards
    Digital wallets
    Replacement cards
    Card programs
    Program management
    Customize your card program
    Add funds to your card program
    Credit Consumer Issuing
    Controls
    Spending controls
    Advanced fraud tools
    3DS
    Fraud challenges
    Real-time authorizations
      Quickstart
      Migrate to direct webhook response
    PIN management
    Issuing Elements
    Token Management
    Funding
    Balance
    Postfund your integration with Stripe
    Postfund your integration with Dynamic Reserves
    Purchases
    Authorizations
    Transactions
    Disputes
    Testing
    Merchant categories
    ATM Usage
    Issuing with Connect
    Set up an Issuing and Connect integration
    Update terms of service acceptance
    Connect funding
    Connected accounts, cardholders, and cards
    Embed card management UI
    Credit
    Overview
    Set up connected accounts
    Manage credit terms
    Report other credit decisions and manage AANs
    Report required regulatory data for credit decisions
    Manage account obligations
    Test credit integration
    Additional information
    Choose a cardholder type
    Customer support for Issuing and Treasury
    Issuing watchlist
    Marketing guidance (Europe/UK)
    Product and marketing compliance guidance (US)
Treasury
Manage money
HomeMoney managementIssuing cards

Issuing real-time authorizations

Learn about real-time authorizations.

Copy page

Webhooks

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

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-VersionrequiredSee API Versioning for supported values.
Content-TypeoptionalThe only content type accepted for Authorization webhook responses is application/json.

Body:

field name required or optional type description
approvedrequiredBooleanSet true to approve an authorization and false to decline.
amountoptionalIntegerIf 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.
metadataoptionalSet of key-value pairsThis can be useful for storing additional information about the object in a structured format.
send_fraud_challenges Public previewoptionalArray of stringsYou 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
Ruby
# Using Sinatra. require 'sinatra' require 'stripe' set :port, 4242 # Set your secret key. Remember to switch to your live secret key in production. # See your keys here: https://dashboard.stripe.com/apikeys Stripe.api_key =
'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

Make an API call Deprecated

This documentation is maintained for existing users. If you’re a new user, respond directly to the webhook. If you’re an existing user, plan to migrate to the direct webhook response. You can follow our direct webhook migration guide.

Make an API call to either approve or decline the request and include the Authorization ID. If you use this method, your webhook must approve or decline each authorization before responding to the incoming webhook request.

server.rb
Ruby
# Using Sinatra. require 'sinatra' require 'stripe' set :port, 4242 # Set your secret key. Remember to switch to your live secret key in production. # See your keys here: https://dashboard.stripe.com/apikeys Stripe.api_key =
'sk_test_BQokikJOvBiI2HlWgH4olfQ2'
# Uncomment and 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'] handle_authorization(auth) end status 200 end def handle_authorization(auth) # Authorize the transaction authorization = Stripe::Issuing::Authorization.approve(auth["id"]) end

We recommend that you only use one of these two methods to respond to authorization requests. For users migrating from one method to another, both methods are supported during a migration. In the event both methods are used on the same authorization, the API call takes precedence over the direct response. For migrations, we recommend only using one method on a given request at a time.

If Stripe doesn’t receive your approve or decline response or request within 2 seconds, the Authorization is automatically approved or declined based on your timeout settings.

Note

If your Issuing balance has insufficient funds for the incoming authorization, the authorization will be denied and your webhook endpoint will not 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. Once 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. Once you have it installed, 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.

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.

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.

To configure Stripe Autopilot, contact Stripe support.

Fraud challenges Public preview

Fraud challenges allow your cardholders to retry non-fraudulent transactions that would have otherwise been blocked.

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.

Fraud challenges are currently limited to beta users. You must be an Issuing customer to join the beta. To request access to the beta, log in to your Stripe account and refresh the page. Contact Stripe for more information.

Enriched merchant data Private preview

The enriched_merchant_data hash on Issuing authorization webhooks passes more comprehensive merchant data in events, such as:

  • Merchant categories
  • Location data
  • Third parties

You can use these details to build more robust authorization logic and downstream user interfaces.

Common use case examples include:

  • Creating real time spend controls
  • Automating transaction categorization
  • Developing better fraud detection and prevention
Was this page helpful?
YesNo
Need help? Contact Support.
Join our early access program.
Check out our changelog.
Questions? Contact Sales.
LLM? Read llms.txt.
Powered by Markdoc