Back to Blog
Salesforce

How to Get Stripe API Keys and Configure Webhooks for a Salesforce Integration

RS

Rajat Sharma

Technical Delivery Head

9 min read - Jul 11, 2026

Stripe Will Not Talk to Salesforce Until You Do This

Every Stripe to Salesforce integration starts in the same place, and it is not Salesforce. Before your org can create a Checkout Session or react to a successful charge, Stripe needs two credentials from you: an API key that authenticates every outbound call to api.stripe.com, and a webhook signing secret that proves each inbound event really came from Stripe, not from whoever found your public endpoint URL.

This article is the zoomed-in first step of our full technical guide at cloudsheer.com/integrations/stripe/technical-guide. One thing you will not do here is OAuth: this pattern uses API key Bearer auth outbound and signed webhooks inbound, so there is no redirect URI to register - just secrets, some shown exactly once.

What You Need Before You Start

  • A Stripe account with two-factor authentication already enabled; creating or revealing a live key triggers a mandatory 2FA dialog.
  • Admin access to the Stripe Dashboard at dashboard.stripe.com.
  • A Salesforce org with My Domain deployed where you hold Customize Application, for Named Credentials and custom metadata.
  • The ability to create a Salesforce Site, because Stripe must reach your webhook handler without logging in.
  • A password manager entry ready for two values: a restricted key starting rk_ and a signing secret starting whsec_. No email, no Slack, no spreadsheet, ever.

Step 1: Find Your API Keys in the Stripe Dashboard

Log in at dashboard.stripe.com, open the Developers menu, and click API keys - or go straight to dashboard.stripe.com/apikeys for live mode and dashboard.stripe.com/test/apikeys for sandbox. On newer accounts this opens Workbench, which replaced the legacy Developers Dashboard; the tilde key opens Workbench from anywhere.

  • Check the mode toggle before copying anything. Sandbox keys are prefixed pk_test_, sk_test_, rk_test_; live keys pk_live_, sk_live_, rk_live_. Each mode has separate keys, and objects created in one are invisible in the other.
  • The publishable pk_ key is client-side only and unused here. The raw sk_ secret key grants full account access, which is exactly why it is not going into Salesforce.

Step 2: Create a Restricted API Key

Stripe now explicitly recommends restricted keys over raw secret keys, and accounts created before May 2026 typically start with none. Permissions come in exactly three levels - None, Read, or Write - and Write implies Read.

  • On the API keys tab, click Create restricted key and name it in the Key name field, something like Salesforce Prod.
  • Least privilege for this build: Checkout Sessions = Write, Charges = Read, Customers = Write only if your flow creates or updates customers, everything else = None. Add Webhook Endpoints = Write only if you manage endpoints via API.
  • Click Create key, complete the two-factor verification dialog, then click the key value to copy it into your password manager immediately - Stripe's warning is literal: Save the key value. You can't retrieve it later. Note where you stored it in the Add a note field and click Done.
  • Later, the overflow menu (...) offers Edit key and Rotate key. Rotating shows an Expiry dropdown: Now kills the old key instantly, or a delayed expiry keeps old and new working for up to 7 days - your zero-downtime window for updating Salesforce.

Step 3: Create the Webhook Event Destination

Open the Webhooks tab in Workbench at dashboard.stripe.com/webhooks and click Create an event destination. On the legacy UI the same flow reads Developers > Webhooks > Add endpoint.

  • Choose the events scope Your account, pin the API version for payloads, then search the event list and tick checkout.session.completed and charge.succeeded - nothing more; every extra event is traffic your endpoint must verify and discard.
  • Click Continue, select Webhook endpoint as the destination type, click Continue again, then paste the Endpoint URL - the public Site URL you will build in Step 6, format https://yourdomain.my.salesforce-sites.com/services/apexrest/stripe/webhook - and create the destination.
  • Register the exact final URL: Stripe does not follow redirects, and any 3xx response is recorded as a delivery failure.

Step 4: Reveal and Store the Signing Secret

Select the new endpoint on the Webhooks tab. Under Signing secret, click Click to reveal and copy the whsec_ value into your password manager.

  • Unlike API keys, it can be re-revealed anytime, but it is unique per endpoint and different in test versus live mode even at the same URL. Pasting the test whsec_ into production fails every live verification.
  • If it ever leaks, choose Roll secret from the endpoint's overflow menu (...). Expire the old secret immediately or delay up to 24 hours, during which Stripe sends one signature per active secret.

Step 5: Wire the Key into Salesforce with an External Credential and Named Credential

In Salesforce Setup, type Named Credentials into Quick Find and open the External Credentials tab. This is the wiring Cloudsheer has deployed on all 14 client projects running this integration.

  • Click New. Label and Name: Stripe. Authentication Protocol: Custom. Save.
  • Under Principals, click New. Identity Type: Named Principal. Add an Authentication Parameter named SecretKey with your rk_ key as the value. Save. The value is write-only after save - Salesforce never displays it again.
  • Under Custom Headers, click New. Name: Authorization. Value: {!'Bearer ' & $Credential.Stripe.SecretKey}. A typo here surfaces later as a 401, not a validation error.
  • On the Named Credentials tab, click New. Label: Stripe_API. URL: https://api.stripe.com. Select the Stripe external credential. Then the two checkboxes behind most support tickets: uncheck Generate Authorization Header (your custom header supplies it) and check Allow Formulas in HTTP Header (or the merge field goes out blank).
  • Map a permission set to the External Credential, grant it Read on the User External Credentials object, and assign it to every calling user, including the automated user behind Queueables and Flows.

Apex now calls callout:Stripe_API/v1/checkout/sessions with no Remote Site Setting needed. One trap: v1 endpoints expect form-encoded bodies, Content-Type application/x-www-form-urlencoded, not JSON.

Step 6: Publish the Webhook Endpoint on a Salesforce Site

  • Write a global class annotated @RestResource(urlMapping='/stripe/webhook') with an @HttpPost method that reads RestContext.request.requestBody - the raw bytes. Never deserialize and re-serialize before verifying; any whitespace change breaks the HMAC.
  • Verify the signature by hand, since there is no official Stripe SDK for Apex: run Crypto.generateMac('hmacSHA256', ...) over t + '.' + rawBody keyed with the whsec_ value, hex-compare against the v1 value in the Stripe-Signature header (format t=<timestamp>, v1=<hex>), and reject timestamps older than 5 minutes.
  • Return 200 before any business logic. Enqueue a Queueable or publish a Platform Event and exit; slow endpoints show up as (Timed out) ERR in Stripe.
  • Go to Setup > Sites (under User Interface > Sites and Domains), register a My Domain-based Site if none exists, set an Active Site Home Page, and Activate. Then click Public Access Settings > Enabled Apex Class Access > Edit and add the webhook class, nothing else - it runs with the guest user's power for any caller on the internet.
  • Store the whsec_ value in a Custom Metadata Type marked Protected Component. Outside a managed package, admins with Customize Application can still view it - acceptable for most deployments, but say so in your security review.

Step 7: Smoke Test Both Directions

Run the loop in sandbox mode before a live key ever touches Salesforce.

  • Outbound: execute anonymous Apex posting form-encoded params to callout:Stripe_API/v1/checkout/sessions. A 200 with a JSON body proves the key, header formula, and permission mapping all work.
  • Inbound: complete a test Checkout with card 4242 4242 4242 4242, or run stripe trigger checkout.session.completed with the Stripe CLI. Then open Workbench > Webhooks > your endpoint > Event deliveries: 200 means delivered, and ERR rows show the response code and next retry time.
  • Retry math: live mode retries with backoff for up to 3 days; sandbox retries only 3 times over a few hours, so a broken test endpoint goes quiet fast. Manual Resend works up to 15 days after the event.

Common Errors and How to Fix Them

  • HTTP 401 Invalid API Key provided: sk_test_****: a rolled key, a wrong-mode key, or pasted whitespace. Fix: re-copy the key for the correct mode and update the principal's parameter.
  • HTTP 401 You did not provide an API key: the Authorization header never rendered. Fix: Allow Formulas in HTTP Header must be checked, Generate Authorization Header unchecked, the formula spelled right, and the running user in the mapped permission set.
  • The provided key 'rk_test_***' does not have the required permissions for this endpoint: the body names the missing permission. Fix: API keys tab > (...) > Edit key, raise that resource to Read or Write.
  • No signatures found matching the expected signature for payload: wrong whsec_ for the endpoint or mode, or a re-serialized body. Fix: verify the untouched RestContext.request.requestBody.toString() and re-reveal the secret for the exact endpoint and mode.
  • Timestamp outside the tolerance zone: clock skew beyond 5 minutes or a replayed event. Fix: keep servers NTP-synced and never set tolerance to 0 - that disables the recency check.
  • System.CalloutException: Unauthorized endpoint, please check Setup->Security->Remote site settings: the code called https://api.stripe.com directly. Fix: call callout:Stripe_API/... instead; no Remote Site Setting is needed.
  • (Unable to connect) or (400/404) ERR in Event deliveries: the guest user lacks class access (Salesforce serves an Authorization Required page), the Site is inactive, or the path is wrong. Fix: activate the Site, grant class access, confirm /services/apexrest/stripe/webhook.
  • (302) ERR: the endpoint answered with a redirect, and Stripe treats every 3xx as failure. Fix: register the final resolved URL, watching for http to https and www rewrites.

Security Checklist Before You Ship

  • Salesforce holds a restricted rk_ key with least-privilege permissions, never a raw sk_ secret key.
  • Every webhook request is signature-verified against the raw body with the 5 minute tolerance enforced, before any processing.
  • The Site guest user can reach exactly one Apex class and holds only the object permissions the async handler needs.
  • A rotation runbook exists: Rotate key allows up to 7 days of old-plus-new overlap, Roll secret up to 24 hours, and Salesforce is updated inside those windows.
  • Test and live values never cross: rk_test_ and the test whsec_ in sandboxes, rk_live_ and the live whsec_ in production.
  • Stripe's API access policies can restrict a key by country or threat category; skip IP pinning, because Salesforce egress IPs vary by Hyperforce region.

Where This Fits in the Full Stripe Build

Credentials and webhooks are the first mile. The full build layers on Checkout Session creation from Flow or Apex, idempotent event processing so a retried charge.succeeded never duplicates records, plus refunds, disputes, and reconciliation. The complete walkthrough lives at cloudsheer.com/integrations/stripe/technical-guide - this article is its first step at click-level zoom.

If you build with Claude Code, we packaged the whole pattern into a free skill. Install it with npx skills add shivamgoel-cloudsheer/Claude-skills --skill stripe-salesforce and it scaffolds the External Credential metadata, the Apex webhook verifier, and the smoke tests, so you review instead of retype.

How Cloudsheer Can Help

Cloudsheer has shipped this exact pattern across 14 client projects: restricted key in an External Credential, Bearer auth through a Named Credential, signed webhooks on a public Site endpoint, signing secret in protected custom metadata. We know where the sharp edges are because we have hit every one - the unchecked formula box, the test secret in production, the guest user with too much power.

If you want this stood up in days instead of sprints, or an existing integration audited before your security review finds the gaps, we should talk. Book a free discovery call at cal.com/cloudsheer-consulting/30min.

Want to see how this applies to your business?

Book a free 30-minute call. We will walk through your specific use case and show you what's possible.

Book Free Discovery Call
Ask me anything