# Get account authorization webhook Source: https://docs.slash.com/api-reference/account-authorization-webhook-get get /account/{accountId}/authorization-webhook Retrieve the authorization webhook configuration for an account. This webhook is called during card authorization requests to allow external systems to approve or reject transactions in real-time. **Enterprise Feature**: Authorization webhooks are an enterprise-only feature. Please reach out to [sales@slash.com](mailto:sales@slash.com) to get set up with this functionality. The Authorization Request Event schema shows the shape of the event that is sent to the webhook. # Partially update account authorization webhook Source: https://docs.slash.com/api-reference/account-authorization-webhook-patch patch /account/{accountId}/authorization-webhook Partially update the authorization webhook configuration for an account. Only the provided fields will be updated. **Enterprise Feature**: Authorization webhooks are an enterprise-only feature. Please reach out to [sales@slash.com](mailto:sales@slash.com) to get set up with this functionality. The Authorization Request Event schema shows the shape of the event that is sent to the webhook. # Set account authorization webhook Source: https://docs.slash.com/api-reference/account-authorization-webhook-put put /account/{accountId}/authorization-webhook Create or update the authorization webhook configuration for an account. This will upsert the webhook configuration, creating it if it doesn't exist or updating it if it does. The secret for a webhook remains static after it is initially created. If you would like to retrieve the secret before sending requests, create the authorization webhook in status 'inactive' first and then update it to 'active' after. **Enterprise Feature**: Authorization webhooks are an enterprise-only feature. Please reach out to [sales@slash.com](mailto:sales@slash.com) to get set up with this functionality. The Authorization Request Event schema shows the shape of the event that is sent to the webhook. # List account balances Source: https://docs.slash.com/api-reference/account-balance-get get /account/{accountId}/balance Retrieve a list of balances for an account. The balances are returned in an array, with each item representing a balance for that account. For charge_card accounts, there will be two balances returned: "cash" and "credit". For debit accounts, there will only be one balance returned: "debit". # List accounts Source: https://docs.slash.com/api-reference/account-get get /account List all accounts you have access to # Get account Source: https://docs.slash.com/api-reference/account-get-by-id get /account/{accountId} Get a single account by Id # Authorization Webhook Overview Source: https://docs.slash.com/api-reference/authorization-webhook-overview Use authorization webhooks to approve or decline card transactions in real-time. **Enterprise Feature**: Authorization webhooks are an enterprise-only feature. Please reach out to [sales@slash.com](mailto:sales@slash.com) to get set up with this functionality. ## Overview Authorization webhooks allow you to implement real-time transaction approval logic by receiving authorization requests before transactions are processed. When a card transaction is initiated, Slash will send a webhook to your configured endpoint, allowing you to approve or decline the transaction based on your custom business logic. ## Setup To begin receiving authorization webhook requests, you'll need to configure an authorization webhook URL for your account using the [account authorization webhook endpoints](/api-reference/account-authorization-webhook-put). ## Webhook Headers Every authorization webhook request includes three important headers for security and validation: * **`x-webhook-id`**: A unique identifier for the webhook request * **`x-webhook-timestamp`**: The timestamp when the webhook was sent (Unix timestamp) * **`x-webhook-signature`**: HMAC signature for request validation ## Request Validation ### Timestamp Validation The `x-webhook-timestamp` should be within 2 minutes of receiving the request to prevent replay attacks. ### Signature Validation To verify that the webhook request actually came from Slash, you should validate the `x-webhook-signature` header using HMAC SHA256. The signature is created by: 1. Removing the `whsec_` prefix from your signing secret and base64 decoding it 2. Creating a payload string: `{webhookId}.{webhookTimestamp}.{requestBody}` 3. Generating an HMAC SHA256 signature using the decoded secret 4. Base64 encoding the signature and prefixing with `v1=` Here's a complete example of signature validation: ```javascript theme={null} import crypto from 'crypto'; function validateWebhookSignature(webhookId, webhookTimestamp, signature, body, signingSecret) { // Remove whsec_ prefix from signing secret if present const secretBase64 = signingSecret.replace('whsec_', ''); // Decode the base64 secret to get the actual signing key const decodedSecret = Buffer.from(secretBase64, 'base64'); // Create the payload to sign: webhookId.webhookTimestamp.body const payload = `${webhookId}.${webhookTimestamp}.${body}`; // Generate HMAC SHA256 signature using the decoded secret const expectedSignature = crypto .createHmac('sha256', decodedSecret) .update(payload) .digest('base64'); // Extract signature from header (remove 'v1=' prefix) const receivedSignature = signature.replace('v1=', ''); // Compare signatures using timing-safe comparison return crypto.timingSafeEqual( Buffer.from(expectedSignature, 'base64'), Buffer.from(receivedSignature, 'base64') ); } ``` ## Request Timeout Authorization webhook requests have a default timeout of **1.5 seconds**. Your endpoint must respond within this timeframe, or the transaction will be automatically decisioned based on your [fallback behavior](#fallback-behavior). ## Response Format Your webhook endpoint must return a **2xx HTTP status code** to indicate successful processing. Any non-2xx response will be treated as a failed request. The response body must contain your authorization decision. See the [Authorization Webhook Response schema](/api-reference/schema-authorization-webhook-response) for the complete response format. Example approval response: ```json theme={null} { "approved": true, "reason": "Transaction approved" } ``` Example decline response: ```json theme={null} { "approved": false, "reason": "Insufficient funds" } ``` ## Event Schema The webhook request body contains detailed information about the authorization request. See the [Authorization Request Event schema](/api-reference/schema-authorization-request-event) for the complete structure of the event data sent to your webhook. ## Debugging and Monitoring When debugging your authorization webhook behavior, you can view the approval or decline reason directly in the transaction details. This helps you understand how your webhook responses are being processed and troubleshoot any issues. External Webhook Approval Reason ## Fallback Behavior If your webhook endpoint is unreachable, returns a non-2xx status code, returns an invalid response body, or times out, the transaction will be automatically handled based on your webhook config's `fallbackBehavior`: * `default`: Your transaction will continue to go through our authorization flow (balance and limit checks still apply), and will be treated as if you approved it. * `reject`: Your transaction will be declined. The same fallback is applied in the rare case that Slash is operating in a degraded mode where external webhooks cannot be consulted at all — so the fallback you configure fully describes what happens whenever we cannot get an answer from your endpoint. ```json theme={null} { "fallbackBehavior": "reject" } ``` ### Conditional fallback with `when` When using `fallbackBehavior: "default"`, you can optionally restrict which transactions are allowed to continue by providing a `when` condition expression. Transactions matching the condition continue through the standard authorization flow; non-matching transactions are declined. If `when` is omitted, every transaction continues (the default behavior). `when` is a small expression tree: | Node | Shape | Semantics | | - | - | - | | Logical | `{ "operator": "and" \| "or", "left": , "right": }` | Combines two sub-conditions; nestable | | Amount comparison | `{ "operator": "lte" \| "lt" \| "gte" \| "gt" \| "eq", "field": "amountCents", "value": }` | Compares the absolute transaction amount in USD cents | | Set membership | `{ "operator": "in", "field": "merchantCategoryCode" \| "merchantCountry", "values": [, ...] }` | Matches when the transaction's field is one of `values` | * `merchantCategoryCode` is the 4-digit MCC and `merchantCountry` is the ISO 3166-1 alpha-2 country, exactly as delivered on the [Authorization Request Event](/api-reference/schema-authorization-request-event) (`merchant.categoryCode` and `merchant.location.country`). * A condition over a field the authorization request did not carry evaluates to false (the transaction is declined rather than allowed through). For example, to auto-approve fallback transactions only when they are at most \$20.00 **and** from MCC 7311: ```json theme={null} { "fallbackBehavior": "default", "when": { "operator": "and", "left": { "operator": "lte", "field": "amountCents", "value": 2000 }, "right": { "operator": "in", "field": "merchantCategoryCode", "values": ["7311"] } } } ``` Or to continue for small transactions **or** any US transaction: ```json theme={null} { "fallbackBehavior": "default", "when": { "operator": "or", "left": { "operator": "lte", "field": "amountCents", "value": 1000 }, "right": { "operator": "in", "field": "merchantCountry", "values": ["US"] } } } ``` The `when` condition only applies to the fallback path — if your endpoint responds in time with a valid decision, that decision is always respected. See the [Authorization Webhook schema](/api-reference/schema-authorization-webhook) for the full config format. # Create book transfer Source: https://docs.slash.com/api-reference/book-transfer-post post /transfers/book-transfer Initiate a *book transfer* — an instant, intra‐Slash money movement between two accounts the caller has access to. `from` and `to` accept any Slash account id the caller can address: - `sa_group_*` — a Slash account group's primary cash account - `sub_*` — a virtual account inside a Slash account group The legal entity owning `from` must equal the request's `x-legal-entity` header (the source-scope guard for user-scoped API keys), and the caller must be a member of both `from` and `to`'s legal entities. Cross-legal- entity transfers between entities you own are supported; sending to a third party is not (see closed-loop-payment, coming soon). Payment is settled instantly on Slash's ledger — no banking-network rail is touched. For ACH/wire/RTP, use the rail-specific endpoint. # List cards Source: https://docs.slash.com/api-reference/card-get get /card List all cards you have access to. # Retrieve card Source: https://docs.slash.com/api-reference/card-get-by-id get /card/{cardId} Fetch details for a single card by card ID To reveal the full PAN or CVV of the card, you must: 1. Set the query params `include_pan=true` and/or `include_cvv=true`. 2. Set the base URL of the request to `https://vault.slash.com`. ```bash theme={null} curl --request GET \ --url "https://vault.slash.com/card/?include_pan=true&include_cvv=true" \ --header 'X-API-Key: ' ``` # List card groups Source: https://docs.slash.com/api-reference/card-group-get get /card-group Retrieve the list of card groups that the user owns. # Get card group Source: https://docs.slash.com/api-reference/card-group-get-by-id get /card-group/{cardGroupId} Fetch details for a single card group by card group ID # Update card group Source: https://docs.slash.com/api-reference/card-group-patch patch /card-group/{cardGroupId} Update a card group # Create card group Source: https://docs.slash.com/api-reference/card-group-post post /card-group Create a card group. # Apply a partial update to a card group's spending constraint Source: https://docs.slash.com/api-reference/card-group-spending-constraint-patch patch /card-group/{cardGroupId}/spending-constraint Apply a partial update to a card group's spending constraint while preserving current properties applied. Properties set to "null" will be removed from the spending constraint. # Replace a card group's spending constraint Source: https://docs.slash.com/api-reference/card-group-spending-constraint-put put /card-group/{cardGroupId}/spending-constraint Fully replace a card group's spending constraint. # Get card group utilization Source: https://docs.slash.com/api-reference/card-group-utilization-get get /card-group/{cardGroupId}/utilization Get a card group's current utilization # Get card modifiers Source: https://docs.slash.com/api-reference/card-modifier-get get /card/{cardId}/modifier Get all modifiers for a card. **Enterprise Feature**: Card modifiers are an enterprise-only feature. Please reach out to [sales@slash.com](mailto:sales@slash.com) to get set up with this functionality. Retrieve all modifiers configured for a specific card. This endpoint returns an array of all active modifiers and their current values. # Set card modifier Source: https://docs.slash.com/api-reference/card-modifier-put put /card/{cardId}/modifier Set a modifier for a card. **Enterprise Feature**: Card modifiers are an enterprise-only feature. Please reach out to [sales@slash.com](mailto:sales@slash.com) to get set up with this functionality. Set a specific modifier for a card. This endpoint allows you to configure individual card behaviors and restrictions. ## Usage Examples ### Enable recurring payments only ```json theme={null} { "name": "only_allow_recurring_payments", "value": true } ``` ### Disable recurring payments restriction ```json theme={null} { "name": "only_allow_recurring_payments", "value": false } ``` # Update card Source: https://docs.slash.com/api-reference/card-patch patch /card/{cardId} Update a card # Create card Source: https://docs.slash.com/api-reference/card-post post /card Create a card # List all card products Source: https://docs.slash.com/api-reference/card-product-get get /card-product Get all card products # Apply a partial update to a card's spending constraint Source: https://docs.slash.com/api-reference/card-spending-constraint-patch patch /card/{cardId}/spending-constraint Apply a partial update to a card's spending constraint while preserving current properties applied. Properties set to "null" will be removed from the spending constraint. # Replace a card's spending constraint Source: https://docs.slash.com/api-reference/card-spending-constraint-put put /card/{cardId}/spending-constraint Fully replace a card's spending constraint. # Get card utilization Source: https://docs.slash.com/api-reference/card-utilization-get get /card/{cardId}/utilization Get a card's current utilization # List cardholders Source: https://docs.slash.com/api-reference/cardholder-get get /cardholder List cardholders for the caller's legal entity. Use the returned `id` as `cardholderId` when creating a card. # Check ACH debit recurring status Source: https://docs.slash.com/api-reference/contact-ach-debit-recurring-get get /contact/{contactId}/ach-debit-recurring Check whether a contact has an active recurring ACH debit authorization. When active, the issuer can automatically debit the payer's bank account for future invoices with auto-pull enabled. # List contacts Source: https://docs.slash.com/api-reference/contact-get get /contact List all contacts for your legal entity. Contacts represent counterparties (customers, vendors) that can be billed via invoices. # Get contact Source: https://docs.slash.com/api-reference/contact-get-by-id get /contact/{contactId} Get a single contact by ID. # Update contact Source: https://docs.slash.com/api-reference/contact-patch patch /contact/{contactId} Update a contact's information. # Create contact Source: https://docs.slash.com/api-reference/contact-post post /contact Create a new contact for your legal entity. Contacts are used as invoice recipients. # Check ACH debit recurring status Source: https://docs.slash.com/api-reference/customer-ach-debit-recurring-get get /customer/{customerId}/ach-debit-recurring Check whether a customer has an active recurring ACH debit authorization. When active, the issuer can automatically debit the payer's bank account for future invoices with auto-pull enabled. # List customers Source: https://docs.slash.com/api-reference/customer-get get /customer List all customers for your legal entity. Customers represent billing recipients for invoices. # Get customer Source: https://docs.slash.com/api-reference/customer-get-by-id get /customer/{customerId} Get a single customer by ID. # Update customer Source: https://docs.slash.com/api-reference/customer-patch patch /customer/{customerId} Update a customer's information. # Create customer Source: https://docs.slash.com/api-reference/customer-post post /customer Create a new customer for your legal entity. Customers are used as invoice recipients. # Approve expense report Source: https://docs.slash.com/api-reference/expense-report-approve-post post /expense-report/{expenseReportId}/approve Approve an expense report and initiate payout. # Bulk action expense reports Source: https://docs.slash.com/api-reference/expense-report-bulk-action-patch patch /expense-report/bulk-action Perform bulk actions on expense reports. # List expense reports Source: https://docs.slash.com/api-reference/expense-report-get get /expense-report List expense reports for your legal entity. Supports pagination, filtering by status and submitter, and sorting. # Get expense report Source: https://docs.slash.com/api-reference/expense-report-get-by-id get /expense-report/{expenseReportId} Get a single expense report by ID. # Update expense report Source: https://docs.slash.com/api-reference/expense-report-patch patch /expense-report/{expenseReportId} Update an existing expense report. Can only update reports with status `pending_review`. Set `status` to `cancelled` to cancel the report. The acting user is derived from your credentials. Bearer tokens and user-scoped API keys may update reports submitted by that user, or any report in the legal entity if that user holds the `reimbursements.approve` permission. Legal-entity API keys must hold `reimbursements.approve`. # Create expense report Source: https://docs.slash.com/api-reference/expense-report-post post /expense-report Submit a new expense report for reimbursement. # Upload receipt Source: https://docs.slash.com/api-reference/expense-report-receipt-post post /expense-report/receipt Upload a receipt image for an expense report. The receipt will be parsed using AI to extract amount, date, and merchant information. Accepts PNG, JPEG, WebP, and PDF formats. # Get reimbursement sources Source: https://docs.slash.com/api-reference/expense-report-reimbursement-sources-get get /expense-report/reimbursement-sources Get connected bank accounts for a user that can be used as reimbursement payout destinations. Pass the `userId` query parameter to specify which user's bank accounts to retrieve. # Reject expense report Source: https://docs.slash.com/api-reference/expense-report-reject-post post /expense-report/{expenseReportId}/reject Reject an expense report. # List invoices Source: https://docs.slash.com/api-reference/invoice-get get /invoice List all invoices for your legal entity. Supports pagination and filtering by status, customer, and account. # Get invoice Source: https://docs.slash.com/api-reference/invoice-get-by-id get /invoice/{invoiceId} Get a single invoice by ID. # Update invoice Source: https://docs.slash.com/api-reference/invoice-patch patch /invoice/{invoiceId} Update an existing invoice. You can update the account, details, payment methods, and auto-pull settings. # Get invoice payment details Source: https://docs.slash.com/api-reference/invoice-payment-details-get get /invoice/{invoiceId}/payment-details Get payment details for a single invoice. # Create invoice Source: https://docs.slash.com/api-reference/invoice-post post /invoice Create a new invoice. # Reconcile invoice Source: https://docs.slash.com/api-reference/invoice-reconcile-post post /invoice/{invoiceId}/reconcile Manually reconcile (link) transactions to an invoice. This marks the invoice as having received payment for the linked transactions. # Cancel invoice series Source: https://docs.slash.com/api-reference/invoice-series-cancel-post post /invoice/series/{invoiceSeriesId}/cancel Cancel an invoice series. Already-generated invoices are not affected. The next scheduled (unpaid) invoice will be voided. # List invoice series Source: https://docs.slash.com/api-reference/invoice-series-get get /invoice/series List all invoice series (recurring invoice configurations) for your legal entity. Supports pagination and filtering by status, customer, and account. # Get invoice series Source: https://docs.slash.com/api-reference/invoice-series-get-by-id get /invoice/series/{invoiceSeriesId} Get a single invoice series by ID. # Update invoice series Source: https://docs.slash.com/api-reference/invoice-series-patch patch /invoice/series/{invoiceSeriesId} Update an existing invoice series. You can update the recurrence rule, payment terms, template, payment methods, and end condition. The contact cannot be changed after creation. # Create invoice series Source: https://docs.slash.com/api-reference/invoice-series-post post /invoice/series Create a new invoice series (recurring invoice). The series will automatically generate invoices according to the specified recurrence rule. # Skip next in series Source: https://docs.slash.com/api-reference/invoice-series-skip-next-post post /invoice/series/{invoiceSeriesId}/skip-next Skip the next scheduled occurrence in a series. The scheduled invoice is voided and the series advances to the following occurrence. # Get invoice settings Source: https://docs.slash.com/api-reference/invoice-settings-get get /invoice/settings Get the current invoice display settings for your legal entity. # Upload invoice logo Source: https://docs.slash.com/api-reference/invoice-settings-logo-post post /invoice/settings/logo Upload a logo image for invoice branding. Accepts PNG, JPEG, SVG, and WebP formats up to 2MB. # Update invoice settings Source: https://docs.slash.com/api-reference/invoice-settings-put put /invoice/settings Update invoice display settings for your legal entity. These settings control the branding and default values used when creating invoices. # Unreconcile invoice Source: https://docs.slash.com/api-reference/invoice-unreconcile-post post /invoice/{invoiceId}/unreconcile Unreconcile (unlink) transactions from an invoice. This removes the payment association for the specified transactions. # Validate invoice number Source: https://docs.slash.com/api-reference/invoice-validate-number-get get /invoice/validate-number Check whether an invoice number is available (not already in use) for your legal entity. # Void invoice Source: https://docs.slash.com/api-reference/invoice-void-post post /invoice/{invoiceId}/void Void an invoice. A voided invoice can no longer be paid. # List legal entities Source: https://docs.slash.com/api-reference/legal-entity-get get /legal-entity List all legal entities you have access to # Account Source: https://docs.slash.com/api-reference/schema-account # Authorization Request Event Source: https://docs.slash.com/api-reference/schema-authorization-request-event **Enterprise Feature**: Authorization webhooks are an enterprise-only feature. Please reach out to [sales@slash.com](mailto:sales@slash.com) to get set up with this functionality. This schema shows the shape of the event that is sent to the authorization webhook. # Authorization Webhook Source: https://docs.slash.com/api-reference/schema-authorization-webhook **Enterprise Feature**: Authorization webhooks are an enterprise-only feature. Please reach out to [sales@slash.com](mailto:sales@slash.com) to get set up with this functionality. # Authorization Webhook Response Source: https://docs.slash.com/api-reference/schema-authorization-webhook-response **Enterprise Feature**: Authorization webhooks are an enterprise-only feature. Please reach out to [sales@slash.com](mailto:sales@slash.com) to get set up with this functionality. This schema defines the expected response format that your authorization webhook endpoint should return to Slash. # Balance Source: https://docs.slash.com/api-reference/schema-balance # Card Source: https://docs.slash.com/api-reference/schema-card # Card Group Source: https://docs.slash.com/api-reference/schema-card-group # Card Group Utilization Source: https://docs.slash.com/api-reference/schema-card-group-utilization # Card Modifier Source: https://docs.slash.com/api-reference/schema-card-modifier **Enterprise Feature**: Card modifiers are an enterprise-only feature. Please reach out to [sales@slash.com](mailto:sales@slash.com) to get set up with this functionality. Card modifiers allow you to configure specific behaviors and restrictions for individual cards. These settings provide fine-grained control over how cards can be used. ## Available Modifiers ### only\_allow\_recurring\_payments When set to `true`, this modifier restricts the card to only allow recurring payments. The default value for newly created cards is `false`. # Cardholder Source: https://docs.slash.com/api-reference/schema-cardholder # Contact Source: https://docs.slash.com/api-reference/schema-contact # Customer Source: https://docs.slash.com/api-reference/schema-customer # Invoice Source: https://docs.slash.com/api-reference/schema-invoice # Invoice Series Source: https://docs.slash.com/api-reference/schema-invoice-series # Legal Entity Source: https://docs.slash.com/api-reference/schema-legalEntity # Spending Constraint Source: https://docs.slash.com/api-reference/schema-spendingConstraint # Transaction Source: https://docs.slash.com/api-reference/schema-transaction # Virtual Account Source: https://docs.slash.com/api-reference/schema-virtualAccount # Webhook Source: https://docs.slash.com/api-reference/schema-webhook # Webhook Event Source: https://docs.slash.com/api-reference/schema-webhook-event A webhook event is sent when an entity in our system changes state. Select an event type below to see the exact payload it delivers. Webhook events contain metadata about the change (entity ID, event type, event ID, and event timestamp) but do not include the actual entity data. To get the current state of the entity that triggered the webhook, fetch it from the corresponding GET endpoint using the `entityId`. We send webhooks asynchronously, so by fetching the entity data separately, you always have the most up-to-date information from a single source of truth. # Get transaction aggregations Source: https://docs.slash.com/api-reference/transaction-aggregation-get get /transaction/aggregation Get transaction aggregations # List transactions Source: https://docs.slash.com/api-reference/transaction-get get /transaction Get all transactions # Get transaction Source: https://docs.slash.com/api-reference/transaction-get-by-id get /transaction/{transactionId} Fetch details for a single transaction by ID # Get transaction fee details Source: https://docs.slash.com/api-reference/transaction-get-fee-details get /transaction/{transactionId}/fee-details Fetch breakdown of a fee transaction # Add or update a note for a transaction Source: https://docs.slash.com/api-reference/transaction-note-patch patch /transaction/{transactionId}/note Update note for a transaction # Get Crypto offramp addresses Source: https://docs.slash.com/api-reference/transfer-crypto-offramp post /crypto/offramp # Create virtual account transfer Source: https://docs.slash.com/api-reference/transfer-virtual-account-post post /transfer/virtual-account Transfer money between Virtual Accounts or fund a Virtual Account from a Primary Account # List virtual accounts Source: https://docs.slash.com/api-reference/virtual-account-get get /virtual-account Retrieve a list of virtual accounts under an account. The virtual accounts are returned in an array, with each item representing a virtual account under that account. The primary account will also be returned as a virtual account, with an account type of 'primary' while actual subaccounts under the primary account will have an account type of 'default'. # Get virtual account Source: https://docs.slash.com/api-reference/virtual-account-get-by-id get /virtual-account/{virtualAccountId} Retrieve a single virtual account by its ID. # Create virtual account Source: https://docs.slash.com/api-reference/virtual-account-post post /virtual-account # Update virtual account Source: https://docs.slash.com/api-reference/virtual-account-update patch /virtual-account/{virtualAccountId} Update an existing virtual account # Changelog Source: https://docs.slash.com/changelog The latest changes to the Slash API. ## 2026-09-16 * Deprecated `virtualAccountId` on the card group object, the `virtualAccountId` parameter of `POST /card-group`, and the `filter:virtualAccountId` query parameter of `GET /card-group`. Card groups are owned by the legal entity and are no longer associated with a virtual account; `virtualAccountId` is now nullable and returns the legacy association where one exists, otherwise `null`. These fields will be removed in a future version. ## 2026-06-16 * Added `sepaInfo`, `pixInfo`, `speiInfo`, and `fpsInfo` to the `Transaction` object. These fields expose payment metadata (sender/recipient name, reference, IBAN/CLABE/sort code, BIC, UETR, payment scheme) for inbound Global USD deposits over SEPA, PIX, SPEI, and Faster Payments. Previously this data was visible on the dashboard but not returned by `GET /transaction` or `GET /transaction/{transactionId}`. ## 2026-03-05 * Added Agent Requests for AI agent integration with human-in-the-loop approval: * `GET /agent-request` - List agent requests created by your API key * `GET /agent-request/{agentRequestId}` - Get the status and details of an agent request * Read-only API keys can now make write requests that are deferred for approval instead of rejected * See the [Agent Requests guide](/docs/agent-requests) for details * Added `POST /analytics` endpoint for querying transaction analytics ## 2026-03-02 * Added Invoice endpoints for programmatic invoice management: * `GET /invoice` - List invoices with filtering by status, customer, and account * `POST /invoice` - Create an invoice * `GET /invoice/{invoiceId}` - Get a single invoice * `PATCH /invoice/{invoiceId}` - Update an invoice * `POST /invoice/{invoiceId}/void` - Void an invoice * `POST /invoice/{invoiceId}/reconcile` - Link transactions to an invoice * `POST /invoice/{invoiceId}/unreconcile` - Unlink transactions from an invoice * `GET /invoice/settings` - Get invoice display settings * `PUT /invoice/settings` - Update invoice display settings * `POST /invoice/settings/logo` - Upload an invoice logo * `GET /invoice/validate-number` - Check if an invoice number is available * Added Contact endpoints for managing invoice recipients: * `GET /contact` - List contacts * `POST /contact` - Create a contact * `GET /contact/{contactId}` - Get a single contact * `PATCH /contact/{contactId}` - Update a contact * `GET /contact/destination` - List contact destinations with eligible transaction types * `PATCH /contact/destination/{destinationId}` - Update a contact destination * `GET /contact/{contactId}/ach-debit-recurring` - Check ACH debit recurring authorization status ## 2025-09-01 * Added `/card/{cardId}/modifier` endpoints. These endpoints allow you to retrieve and set card modifiers that control specific card behaviors and restrictions. This is only an enterprise feature; reach out to [sales@slash.com](mailto:sales@slash.com) to request access. ## 2025-06-10 * Added `/account/{accountId}/authorization-webhook` endpoint. This endpoint allows you to retrieve, create, and update the authorization webhook for an account. This is only an enterprise feature; reach out to [sales@slash.com](mailto:sales@slash.com) to request access. ## 2025-06-05 * Added `wireInfo` and `achInfo` to the `Transaction` object ## 2025-05-17 * Added required `name` field to the Account schema. ## 2025-05-09 * Added `virtualAccountId` to the card group object. * Added `virtualAccountId` as an optional parameter to the `POST /card-group` request body. ## 2025-02-06 * Added `GET /card/{cardId}/utilization` endpoint. This endpoint allows you get the utilization for a single card not associated with a card group. ## 2025-02-05 * Added `POST /virtual-account/transfer`. This endpoint allows you to transfer funds between virtual accounts. ## 2025-01-31 * Added transactions visible only on transactions page to `GET /transaction` and `GET /transaction/aggregation` * Currently, these types of transactions are limited to transfers between virtual accounts ## 2025-01-28 * Added `filter:cardGroupName` to `GET /card` endpoint. This field allows you to filter cards by card group name. ## 2025-01-23 * Added `filter:name` to `GET /card-group` endpoint. This field allows you to filter card groups by name. * Added `memo` field to `GET /transaction` endpoint. This field contains the memo field for ach transactions and the sender reference for wire transactions. ## 2025-01-19 * Added `PATCH /card/{cardId}/spending-constraint` and `PATCH /card-group/{cardGroupId}/spending-constraint` endpoints. These endpoints allow for partial updating of existing spending constraints while preserving original values. * For consistency, added `PUT /card/{cardId}/spending-constraint` and `PUT /card-group/{cardGroupId}/spending-constraint` endpoints. These endpoints fully replace the existing spending constraint with the new one. ## 2024-12-05 * Added `GET /card-product` endpoint for getting card product information. * Added `cardProductId` field to `POST /card` endpoint for creating a card with a specific card product. ## 2024-12-05 * Added `GET /transaction/aggregation` endpoint for getting aggregated statistics about transactions. ## 2024-12-03 * Added `virtualAccountId` field to card creation request and response. `accountId` is still required, while this field is optional. ## 2024-11-30 * Added `merchantData` field to `Transaction`. This field allows you to read specific details about the merchant, such as its reported description of the transaction, category code and location. Only applicable for card transactions. * Deprecated `merchantDescription` field in `Transaction` in favor of `merchantData.description` ## 2024-11-28 * Added `Virtual Account` endpoints to the API, allowing you to view, update, and close them ## 2024-10-20 * Added `sort` query param to the `GET /card` endpoint. This field allows you to sort cards by creation date or name. * Added `sortDirection` query param to the `GET /card` endpoint. This field allows you to control the direction of the sort. ## 2024-10-03 * Added `cardGroupId` field to card creation request * Added `GET /card-group/{cardGroupId}/utilization` endpoint. This endpoint returns the current utilization for a card group. ## 2024-07-10 * Added `referenceNumber` field to `Transaction` schema, which is returned by the `GET /transaction` and `GET /transaction/{transactionId}` endpoints. ## 2024-07-02 * Added `filter:detailed_status` query param to the `GET /transaction` endpoint. This field allows you to filter transactions by their detailed status. See the [API reference](api-reference/transaction-get) for more details. ## 2024-06-25 * Added `GET /card/{cardId}/events` endpoint. This endpoint returns events from Visa for a single card, including authorizations, reversals, and verifications. See the [API reference](api-reference/card-get-events) for more details. ## 2024-06-20 * Added `userData` field to the `POST /card` and `PATCH /card/{cardId}` requests. This field allows you to attach your own data to the card, like an email address or an internal ID. * Added `detailedStatus` and `declineReason` fields to the `Transaction` schema, which is returned by the `GET /transaction` and `GET /transaction/{transactionId}` endpoints. ## 2024-06-14 * Added `PATCH /transaction/{transactionId}/note` endpoint. This endpoint allows you to add a note to a transaction. See the [API reference](api-reference/transaction-note-patch) for more details. ## 2024-05-01 We've made a number of non-breaking changes to the API. For the most part, you can continue to use the API as you have been, as long as you've been authenticating via API key. Here's what's new: * Added `/legal-entity` endpoint. This endpoint returns the legal entities available to you. You can use this to filter the results of other endpoints. * Added optional `filter:legalEntityId` query params to the following endpoints. This field allows you to specify the legal entity to filter by. If you are authenticating with an API key, this field will default to the legal entity the API key is associated with. If you are authenticating with a user token, you can use this field to filter by legal entity. * `GET /account` * `GET /transaction` * `GET /card` * `GET /webhook` * Added optional `filter:accountId` query params to the following endpoints. This field allows you to specify the account to filter by. * `GET /card` * Renamed the `PayBySlashUser` schema to `SlashHandle`. * Renamed the `sourceSlashAccountGroup` field in `SlashHandle` to `accountId`, which represents the ID of the account that will be used to send and receive funds. * Note that this is a breaking change. If you are relying on this field, please update your code to use the new field name. * Added `GET /slash-handle`. The `GET /pay` endpoint will continue to work, but we recommend updating your code to use the new endpoint, which returns a list instead of a single Slash handle (although the list will usually only contain one element). * Added optional `sourceSlashHandleId` and `legalEntityId` fields to the `POST /pay` request body. This field allows you to specify the legal entity or slash handle to use for the payment. (Each legal entity has one Slash handle.) If you are authenticating with an API key, this field will default to your legal entity's Slash handle. If you are authenticating with a user token, one of these fields is required. * Added optional `legalEntityId` field to the `POST /webhook` request body. This field allows you to specify the legal entity to create the webhook for. If you are authenticating with an API key, this field will default to your legal entity. If you are authenticating with a user token, this field is required. * Added optional `accountId` field to the `POST /card` request body. This field allows you to specify the account to create the card for. If you are authenticating with an API key, this field will default to the first commercial account under your legal entity, but we recommend that you specify the account ID explicitly. If you are authenticating with a user token, this field is required. # MCP Server Source: https://docs.slash.com/docs/mcp Connect AI agents to the Slash API using the Model Context Protocol ## Overview Slash provides an [MCP](https://modelcontextprotocol.io/) server that lets AI agents interact with the Slash API. The server exposes one tool per API endpoint, generated from the Slash OpenAPI spec, through the standard MCP protocol. When an RSA public key is provided, sensitive card data (PAN and CVV) is RSA-encrypted before it reaches the agent, ensuring raw card numbers are never exposed to the AI. If no key is provided, raw card data will be returned as plaintext. ## Connection Parameters | Parameter | Required | Description | | - | - | - | | `apiKey` | Yes | Your Slash API key ([get one here](https://app.slash.com/platinum/settings/api-management?tab=api_keys)). Pass as `?apiKey=` query parameter or `X-API-Key` header. | | `rsaPublicKey` | No | Base64-encoded RSA public key (no PEM markers). Pass as `?rsaPublicKey=` query parameter or `X-RSA-Public-Key` header. When provided, card data (PAN/CVV) is RSA-encrypted before being returned. When omitted, raw card data is returned as plaintext. | ## Available Tools Each JSON endpoint in the Slash API is available as its own tool, named after its HTTP method and path. For example, `GET /account/{accountId}/balance` is `get_account_accountId_balance`, and `GET /legal-entity` is `get_legal_entity`. Use your MCP client's tool list to see every tool with its description and input schema. Every tool accepts the same top-level arguments: | Argument | Description | | - | - | | `pathParams` | Values for the endpoint's path parameters, e.g. `{ "accountId": "..." }`. Required when the path has parameters. | | `query` | Query parameters declared by the endpoint. Undeclared parameters are ignored. | | `headers` | Header parameters declared by the endpoint, such as `X-Idempotency-Key`. Undeclared headers are ignored. | | `body` | Request body, for endpoints that accept one. | | `legalEntityId` | Legal entity to scope the request to. Required for endpoints that operate on a specific legal entity when your key has access to more than one. | These tools replace `list_endpoints`, `get_endpoint_schema`, and `call_api_endpoint`. Instead of passing a method and path to `call_api_endpoint`, call the endpoint's tool directly. For example, `GET /card/{cardId}` is now `get_card_cardId` with `{ "pathParams": { "cardId": "..." } }`. ## Setup by AI Platform Add the following to your Claude Desktop configuration file: * **macOS**: `~/Library/Application Support/Claude/claude_desktop_config.json` * **Windows**: `%APPDATA%\Claude\claude_desktop_config.json` ```json theme={null} { "mcpServers": { "slash": { "url": "https://mcp.slash.com/mcp?apiKey=YOUR_API_KEY" } } } ``` Restart Claude Desktop to apply the changes. Use the Claude Code CLI to add the Slash MCP server: ```bash theme={null} claude mcp add slash https://mcp.slash.com/mcp?apiKey=YOUR_API_KEY ``` Or add it to your project's `.mcp.json` file: ```json theme={null} { "mcpServers": { "slash": { "url": "https://mcp.slash.com/mcp?apiKey=YOUR_API_KEY" } } } ``` 1. Go to **Settings** → **Connected Apps** → **Add Connection** 2. Select **MCP Server** 3. Enter the connection URL: ``` https://mcp.slash.com/mcp?apiKey=YOUR_API_KEY ``` 4. Click **Connect** The Slash tools will now be available in your ChatGPT conversations. Add the Slash MCP server to your Cursor configuration. Open Settings (`Cmd+,` / `Ctrl+,`), go to **Features** → **MCP Servers**, and add: ```json theme={null} { "slash": { "url": "https://mcp.slash.com/mcp?apiKey=YOUR_API_KEY" } } ``` Alternatively, add it to your project's `.cursor/mcp.json`: ```json theme={null} { "mcpServers": { "slash": { "url": "https://mcp.slash.com/mcp?apiKey=YOUR_API_KEY" } } } ``` Configure the Slash MCP server in your Codex settings: ```json theme={null} { "mcp_servers": [ { "name": "slash", "url": "https://mcp.slash.com/mcp?apiKey=YOUR_API_KEY" } ] } ``` Or use the Codex CLI: ```bash theme={null} codex mcp add slash --url "https://mcp.slash.com/mcp?apiKey=YOUR_API_KEY" ``` In the OpenClaw interface, go to **Integrations** → **MCP Servers** and add a new connection: * **Name**: `slash` * **URL**: `https://mcp.slash.com/mcp?apiKey=YOUR_API_KEY` Or configure via the OpenClaw config file: ```json theme={null} { "mcpServers": { "slash": { "url": "https://mcp.slash.com/mcp?apiKey=YOUR_API_KEY" } } } ``` ## Card Data Encryption To encrypt card data before it reaches the agent, generate an RSA key pair and pass the public key: ```bash theme={null} openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out private.pem openssl rsa -in private.pem -pubout -out public.pem ``` Extract the base64 key content (no PEM markers): ```bash theme={null} grep -v '^\-\-\-\-\-' public.pem | tr -d '\n' ``` Then append the `rsaPublicKey` parameter to your connection URL: ``` https://mcp.slash.com/mcp?apiKey=YOUR_API_KEY&rsaPublicKey=YOUR_BASE64_PUBLIC_KEY ``` The `apiKey` and `rsaPublicKey` can also be passed as `X-API-Key` and `X-RSA-Public-Key` headers. When you provide an `rsaPublicKey` and request card details with `include_pan=true` or `include_cvv=true`, the PAN and CVV are returned as RSA-encrypted, base64-encoded ciphertext. The agent never sees the raw card numbers. If no `rsaPublicKey` is provided, the PAN and CVV are returned as plaintext. The encryption uses **RSA-OAEP** with your public key. ### Decrypting card data Use your private key to decrypt the base64-encoded values: ```bash theme={null} echo "ENCRYPTED_BASE64_VALUE" | base64 -d | \ openssl pkeyutl -decrypt -inkey private.pem -pkeyopt rsa_padding_mode:oaep ``` #### Python ```python theme={null} from base64 import b64decode from Crypto.Cipher import PKCS1_OAEP from Crypto.PublicKey import RSA private_key = RSA.import_key(open("private.pem").read()) cipher = PKCS1_OAEP.new(private_key) encrypted_pan = "ENCRYPTED_BASE64_VALUE" pan = cipher.decrypt(b64decode(encrypted_pan)).decode("utf-8") ``` #### Node.js ```typescript theme={null} import { privateDecrypt, constants } from 'crypto'; import { readFileSync } from 'fs'; const privateKey = readFileSync('private.pem', 'utf-8'); const encryptedPan = 'ENCRYPTED_BASE64_VALUE'; const pan = privateDecrypt( { key: privateKey, oaepHash: 'sha1', padding: constants.RSA_PKCS1_OAEP_PADDING }, Buffer.from(encryptedPan, 'base64'), ).toString('utf-8'); ``` Keep your private key secure. It should never be shared, committed to source control, or exposed to the AI agent. # Message Level Encryption (MLE) Source: https://docs.slash.com/docs/message-level-encryption Encrypt API request and response payloads end-to-end. Message Level Encryption (MLE) encrypts the JSON body of an API request and response with a key that only you and Slash hold, adding a second layer of protection on top of TLS. Even if a request or response is logged or intercepted anywhere in between, the payload stays opaque. MLE is: * **Opt-in per request.** Pass your key ID in the `MLE-KEY-ID` header to opt a request in. Requests without the header keep working as plain JSON, even if your entity has active keys — so you can roll MLE out endpoint by endpoint. * **Symmetric.** Payloads are sealed with **AES-256-GCM** using a shared 256-bit secret created in the dashboard. There are no key pairs, JWKs, or JWE envelopes to manage. * **Two-way.** When a request opts in, Slash decrypts your request body with the key and encrypts the response body back to you with the same key. ## The envelope An MLE payload is a single-field JSON object: ```json theme={null} { "encryptedData": "" } ``` | Component | Detail | | - | - | | Cipher | AES-256-GCM | | `iv` | 12 random bytes, generated fresh for every message | | `ciphertext` | The UTF-8 JSON payload, encrypted | | `tag` | 16-byte GCM authentication tag | | Encoding | `base64url` over the concatenation of `iv`, `ciphertext`, `tag` (in that order) | Most AES-GCM implementations (WebCrypto, Node, Python, Java, Go) already emit `ciphertext || tag` as one buffer, so building the envelope is just prepending your IV and base64url-encoding. ## Setup ### 1. Create an encryption key In the [dashboard](https://app.slash.com), go to **Settings → API → Encryption Keys** and click **Create Encryption Key**. You get back two values: * **Key ID** (`tok_...`) — the identifier you pass in the `MLE-KEY-ID` header. Visible in the dashboard at any time. * **Secret** — a base64url-encoded 256-bit AES key. The secret is shown **exactly once**, at creation time. Store it in your secret manager immediately — if you lose it, revoke the key and create a new one. ### 2. Encrypt your request body Decode the secret, then seal the JSON body into the envelope: ```ts Node.js theme={null} import { createCipheriv, randomBytes } from "node:crypto"; const key = Buffer.from(process.env.SLASH_MLE_SECRET, "base64url"); // 32 bytes function encrypt(payload) { const iv = randomBytes(12); const cipher = createCipheriv("aes-256-gcm", key, iv); const ciphertext = Buffer.concat([ cipher.update(JSON.stringify(payload), "utf8"), cipher.final(), ]); return { encryptedData: Buffer.concat([iv, ciphertext, cipher.getAuthTag()]).toString( "base64url" ), }; } ``` ```python Python theme={null} import base64 import json import os from cryptography.hazmat.primitives.ciphers.aead import AESGCM secret = os.environ["SLASH_MLE_SECRET"] key = base64.urlsafe_b64decode(secret + "=" * (-len(secret) % 4)) # 32 bytes def encrypt(payload: dict) -> dict: iv = os.urandom(12) sealed = AESGCM(key).encrypt(iv, json.dumps(payload).encode(), None) # ciphertext || tag encoded = base64.urlsafe_b64encode(iv + sealed).rstrip(b"=").decode() return {"encryptedData": encoded} ``` ### 3. Send the request with `MLE-KEY-ID` Send the envelope as the request body and pass your key ID in the `MLE-KEY-ID` header. Everything else about the request — URL, method, query parameters, authentication — stays exactly the same. ```bash theme={null} curl https://api.slash.com/contact \ -X POST \ -H "X-API-Key: $SLASH_API_KEY" \ -H "MLE-KEY-ID: tok_live_..." \ -H "Content-Type: application/json" \ -d '{ "encryptedData": "9Zw1..." }' ``` GET requests have no body to encrypt — just pass the header and the response comes back encrypted. ### 4. Decrypt the response Responses to opted-in requests are served as plain `application/json` containing the same single-field envelope. Decrypt it with the same key: ```ts Node.js theme={null} import { createDecipheriv } from "node:crypto"; function decrypt({ encryptedData }) { const data = Buffer.from(encryptedData, "base64url"); const decipher = createDecipheriv("aes-256-gcm", key, data.subarray(0, 12)); decipher.setAuthTag(data.subarray(data.length - 16)); const plaintext = Buffer.concat([ decipher.update(data.subarray(12, data.length - 16)), decipher.final(), ]); return JSON.parse(plaintext.toString("utf8")); } ``` ```python Python theme={null} def decrypt(envelope: dict) -> dict: encoded = envelope["encryptedData"] data = base64.urlsafe_b64decode(encoded + "=" * (-len(encoded) % 4)) plaintext = AESGCM(key).decrypt(data[:12], data[12:], None) return json.loads(plaintext) ``` ## Complete example ```ts Node.js theme={null} import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto"; const key = Buffer.from(process.env.SLASH_MLE_SECRET, "base64url"); const MLE_KEY_ID = "tok_live_..."; function encrypt(payload) { const iv = randomBytes(12); const cipher = createCipheriv("aes-256-gcm", key, iv); const ciphertext = Buffer.concat([ cipher.update(JSON.stringify(payload), "utf8"), cipher.final(), ]); return { encryptedData: Buffer.concat([iv, ciphertext, cipher.getAuthTag()]).toString( "base64url" ), }; } function decrypt({ encryptedData }) { const data = Buffer.from(encryptedData, "base64url"); const decipher = createDecipheriv("aes-256-gcm", key, data.subarray(0, 12)); decipher.setAuthTag(data.subarray(data.length - 16)); const plaintext = Buffer.concat([ decipher.update(data.subarray(12, data.length - 16)), decipher.final(), ]); return JSON.parse(plaintext.toString("utf8")); } const res = await fetch("https://api.slash.com/contact", { method: "POST", headers: { "X-API-Key": process.env.SLASH_API_KEY, "MLE-KEY-ID": MLE_KEY_ID, "Content-Type": "application/json", }, body: JSON.stringify( encrypt({ name: "Slash Financial", recipientType: "contact", recipientLegalName: "Slash Financial Inc", recipientEmail: "ap@slash.com", }) ), }); const { contact } = decrypt(await res.json()); console.log("Created contact:", contact.id); ``` ## Card reveals through vault.slash.com MLE works on [card reveals](/api-reference/card-get-by-id) through `vault.slash.com` exactly like on `api.slash.com`: pass `MLE-KEY-ID`, get the same encrypted envelope back, decrypt with the same key. ## Key management * **Multiple active keys** can exist at once, so rotation is zero-downtime: create a new key, move your integration over, then revoke the old one. * **Revoke keys** from the same dashboard page. Requests referencing a revoked key are rejected with `400`. * Key IDs are not secret — only the secret is. Treat the secret like an API key: keep it server-side, never ship it to a browser or mobile client. ## Errors MLE failures use the [standard error envelope](/introduction#errors) and fail closed — an opted-in request is never silently processed or answered in plaintext. | Status | Message | What it means | | - | - | - | | `400` | `MLE-KEY-ID is invalid, revoked, or does not belong to this legal entity` | The key ID is unknown, revoked, or owned by a different legal entity. | | `400` | `MLE-KEY-ID must be provided once` | The header was sent more than once. | | `400` | `Unsupported MLE envelope` | The request body is not a `{ "encryptedData": "..." }` object. | | `400` | `Invalid MLE payload` | `encryptedData` is too short to contain an IV and tag. | | `400` | `Unable to decrypt MLE payload` | Decryption failed — wrong key, corrupted ciphertext, or a failed GCM authentication check. | | `400` | `Decrypted MLE payload is not valid JSON` | Decryption succeeded but the plaintext isn't JSON. | | `400` | `MLE is not supported for endpoints with non-JSON responses. Retry without the MLE-KEY-ID header.` | The endpoint returns a non-JSON body (e.g. a PDF), which can't be wrapped in the JSON envelope. | # SDKs Source: https://docs.slash.com/docs/sdks Official Slash SDKs for Python and Node.js ## Official SDKs Slash offers official SDKs to make integrating with our API easier: * **Python**: [slash-sdk on PyPI](https://pypi.org/project/slash-sdk/) * **Node.js**: [slash-sdk on npm](https://www.npmjs.com/package/slash-sdk) ## Installation ### Python ```bash theme={null} pip install slash-sdk ``` ### Node.js ```bash theme={null} npm install slash-sdk ``` ## Quick Start ### Python ```python theme={null} from slash_sdk import SlashClient client = SlashClient(api_key="your-api-key") # Get your legal entity legal_entity = client.legal_entity.get() ``` ### Node.js ```typescript theme={null} import { SlashClient } from 'slash-sdk'; const client = new SlashClient({ apiKey: 'your-api-key' }); // Get your legal entity const legalEntity = await client.legalEntity.get(); ``` # Getting Started Source: https://docs.slash.com/introduction Use the Slash API to access your account, transactions, cards and more. ## Welcome Our API is currently in Beta and is not yet available to the wider public. More functionality will be added in the near future as we continuously build out our API. If you'd like to request access, offer feedback, or have any inquiries about the API, don't hesitate to contact us at [support@joinslash.com](mailto:support@joinslash.com). ## Accepting payments Looking to take card payments on your own site rather than manage your Slash account? Slash Payment Processing has its own set of guides and endpoints — switch to **Payment Processing** at the top of the sidebar. Create a payment intent, collect the card in the browser, and fulfill once it settles. Drop-in browser components for collecting card details securely. Create, list, and inspect payment intents and their webhooks. Issue full or partial refunds against a settled payment. ## Base URL All requests are made against: ``` https://api.slash.com ``` ## Authorization Slash uses API Keys to authorize requests. If you have beta access to the API, you can create and revoke your API Keys in the dashboard for your organization. ```bash theme={null} curl --url "https://api.slash.com${PATH}" \ -H "X-API-Key: ${SLASH_API_KEY}" ``` Keys come in two flavors: * **Legal-entity-scoped keys** are pinned to a single legal entity. Minted from the dashboard under a specific entity; every request acts on that entity. Use these for server-to-server integrations against one entity. * **User-scoped keys** are pinned to a user and span every legal entity that user has access to. Use these to act as a specific user across one or more entities. ### `x-legal-entity` header (user-scoped keys) Every request made with a user-scoped key must include an `x-legal-entity` header naming the legal entity the request is operating on. The one exception is `GET /legal-entity`, which lists the entities the user can access — use it to discover the id you should send. ```bash theme={null} curl --url "https://api.slash.com/transfers/book-transfer" \ -H "X-API-Key: ${SLASH_API_KEY}" \ -H "x-legal-entity: ${LEGAL_ENTITY_ID}" \ -H "X-Idempotency-Key: $(uuidgen)" \ -H "content-type: application/json" \ -d '{"from":"sa_group_...","to":"sub_...","amountCents":1000}' ``` Requests without the header are rejected with `400`. If the authenticated user does not have an active permission role on the supplied entity, the request is rejected with `403`. ## Idempotency Write endpoints that move money or create resources accept an `X-Idempotency-Key` header. Replaying a request with the same key returns the original result; replaying with the same key but a different body returns `409 Conflict`. Use a fresh UUID per logical operation. Endpoints that currently require an idempotency key: * `POST /transfers/book-transfer` * `POST /transfer/virtual-account` ## Errors Errors are returned as JSON with a consistent envelope: ```json theme={null} { "success": false, "message": "You must be logged in to do that. [4041-2121cyx]", "identifier": 4041, "rawStatus": 401, "displayType": "toast" } ``` | Field | Description | | - | - | | `message` | Human-readable explanation, suitable for surfacing to end users. The bracketed `[-]` suffix uniquely identifies the failure for support. | | `identifier` | Stable numeric error code. Pair with `rawStatus` to branch programmatically. | | `rawStatus` | HTTP status of the response (mirrors the response status line). | | `displayType` | UI hint (`toast`, etc.) used by Slash's own clients; safe to ignore. | | `success` | Always `false` on errors. | Every response — success or failure — also carries an `x-request-id` header. Include it when reporting an issue. Common statuses: | Status | Meaning | | - | - | | `400 Bad Request` | Malformed body, missing required field, or missing `x-legal-entity` on a user-scoped key. | | `401 Unauthorized` | Missing or invalid API key. | | `403 Forbidden` | Authenticated, but the user/entity lacks permission for the resource. | | `404 Not Found` | The referenced resource does not exist (or is not visible to the caller). | | `409 Conflict` | Idempotency key replay with a mismatched body, or a state-conflict on the target resource. | | `500 Internal Server Error` | Something went wrong on our end — retry, and contact support with the `x-request-id` if it persists. | # List agent requests Source: https://docs.slash.com/api-reference/agent-request-get get /agent-request List all agent requests created by this API key. Agent requests are created when a read-only API key makes a write request (POST, PUT, PATCH, DELETE) to the Public API. This endpoint returns all such requests, paginated. # Get agent request Source: https://docs.slash.com/api-reference/agent-request-get-by-id get /agent-request/{agentRequestId} Get the status and details of an agent request by ID. Agent requests are created when a read-only API key makes a write request (POST, PUT, PATCH, DELETE) to the Public API. Instead of executing immediately, these requests are deferred and require approval from an authorized user. Use this endpoint to check the status of a pending request or retrieve the result after execution. # List merchant categories Source: https://docs.slash.com/api-reference/merchant-category-get get /merchant-category Retrieve the list of available merchant category ids # List merchants Source: https://docs.slash.com/api-reference/merchant-get get /merchant Retrieve the list of available merchant ids # Get merchant Source: https://docs.slash.com/api-reference/merchant-get-by-id get /merchant/{merchantId} Fetch details for a single merchant by ID # List payment intents Source: https://docs.slash.com/api-reference/payment-intent-get get /payments/payment-intent List payment intents for your legal entity, newest first. Supports cursor pagination and filtering by status. # Retrieve payment intent Source: https://docs.slash.com/api-reference/payment-intent-get-by-id get /payments/payment-intent/{paymentIntentId} Get a single payment intent by ID. Use this to check whether a payment has settled (`status` becomes `succeeded`) as an alternative or reconciliation fallback to webhooks. # Update payment intent Source: https://docs.slash.com/api-reference/payment-intent-patch patch /payments/payment-intent/{paymentIntentId} Update the fields of a pending payment intent. Payment intents that are processing or in a terminal state cannot be updated. Update the `amount`, `currency`, `metadata`, and/or `allowedPaymentMethods` of a payment intent before payment starts. Send only the fields you want to change. At least one field is required. `metadata` is shallow-merged with existing metadata. Supplied keys are added or overwritten, omitted keys remain unchanged, and setting a top-level key to `null` deletes it. An empty metadata object makes no metadata changes. `allowedPaymentMethods` replaces the intent's allowed payment methods with the list you send. Set it to `null` to allow every supported payment method again, or omit it to leave the current setting unchanged. A buyer who already loaded the payment form should call `fetchUpdates()` to see the change. Only payment intents with `pending` status can be updated. Once processing starts or the payment reaches a terminal state, the endpoint returns `409`. # Create payment intent Source: https://docs.slash.com/api-reference/payment-intent-post post /payments/payment-intent Create a payment intent for collection with the Slash Checkout SDK. When returned, the client secret authorizes the browser to load and confirm only this payment intent and must not be logged or stored in browser persistence. # List refunds Source: https://docs.slash.com/api-reference/refund-get get /payments/refund List refunds for your legal entity, newest first. Supports cursor pagination and filtering by payment intent and status. # Retrieve refund Source: https://docs.slash.com/api-reference/refund-get-by-id get /payments/refund/{refundId} Get a single refund by ID. # Create refund Source: https://docs.slash.com/api-reference/refund-post post /payments/refund Refund all or part of a succeeded payment. Optionally provide an `X-Idempotency-Key` header so retries with the same key and request return the existing refund. # Payment Intent Source: https://docs.slash.com/api-reference/schema-payment-intent A payment intent represents a single intent to collect a card payment from a buyer. It is the object that actually tracks the money: how much is being collected, and where that collection stands — from `pending` (nothing paid yet) through `processing` (a payment is in flight) to a terminal `succeeded` or `canceled`. Create one with [`POST /payments/payment-intent`](/api-reference/payment-intent-post) and collect it with [Payment Elements](/docs/payments/payment-elements). Payment intent responses include a `clientSecret` scoped to that intent. It is the browser's credential for that intent: it authorizes loading the payment form and confirming the payment, which advances the intent from `pending` to `processing`. It grants nothing else. Treat the client secret as sensitive. Pass it to the browser, but never log it or persist it. ## Statuses | Status | Meaning | | - | - | | `pending` | Created, no payment received yet. | | `processing` | A buyer completed the payment form; settlement is in flight. A declined card returns the intent to `pending` so the buyer can try again. | | `succeeded` | The payment settled. This is the terminal success state. | | `canceled` | The intent was canceled and can no longer be paid. | ## Metadata `metadata` is stored on the intent and echoed back on every read — use it to carry your own order reference (for example `{ "orderId": "ord_123" }`) so you can correlate a payment intent to the right order without extra lookups. ## Payment methods By default a payment intent offers every supported payment method: `card`, `apple_pay`, and `google_pay`. To offer only some of them, pass `allowedPaymentMethods` when you create or update the intent, for example `["card"]` to turn off wallets for a single payment. `allowedPaymentMethods` is returned exactly as you set it, and is omitted when every supported method is allowed. Wallets such as Apple Pay and Google Pay only appear on devices and browsers that support them. ## Tracking payment state Subscribe to the [`payments.payment_intent.updated`](/docs/payments/webhooks/payment-intent-updated) webhook and read the intent back with [`GET /payments/payment-intent/{id}`](/api-reference/payment-intent-get-by-id) when it fires. If you cannot receive webhooks, poll that endpoint instead. Fulfill when `status` is `"succeeded"` — that is the only signal a payment has settled; never grant goods or services on a browser-side signal alone. # Refund Source: https://docs.slash.com/api-reference/schema-refund A refund returns some or all of a captured payment to the buyer. Refunds are created against a payment intent and are processed asynchronously: a refund starts as `pending`, moves to `processing`, and ends in a terminal `succeeded` or `failed` state. ## Statuses | Status | Meaning | | - | - | | `pending` | The refund was accepted by `POST /payments/refund`. No money has moved yet. | | `processing` | Slash has picked up the refund and is submitting it to the card network. | | `succeeded` | The refund was accepted by the card network. This is the terminal success state; when the funds appear on the buyer's statement is up to their bank. | | `failed` | The refund was rejected and no money moved. The amount is released back to the payment's refundable balance, so you can retry with a new `POST /payments/refund`. | ## Creating refunds [`POST /payments/refund`](/api-reference/refund-post) requires the `paymentIntentId` to refund and the `amount` in the smallest unit of the payment intent's currency. Refunds are always issued in the currency of the original payment — you cannot pass a different one. You can refund all or part of a payment by setting `amount` to the desired value. You can create multiple partial refunds against the same payment; the total across all refunds can never exceed the captured amount. Refund creation supports [idempotent requests](/docs/payments/idempotent-requests) via the optional `X-Idempotency-Key` header. ## Tracking refund state Subscribe to the [`payments.refund.created`](/docs/payments/webhooks/refund-created) and [`payments.refund.updated`](/docs/payments/webhooks/refund-updated) webhooks to be notified when a refund is created and on every status change, then read the refund back with [`GET /payments/refund/{refundId}`](/api-reference/refund-get-by-id). If you cannot receive webhooks, poll [`GET /payments/refund/{refundId}`](/api-reference/refund-get-by-id) until `status` is terminal. # Get Token Prices Source: https://docs.slash.com/api-reference/tokens-prices-get get /tokens/prices Returns real-time prices for major crypto tokens. This endpoint is publicly accessible without authentication. # List webhooks Source: https://docs.slash.com/api-reference/webhook-get get /webhook Get all webhooks # Get webhook Source: https://docs.slash.com/api-reference/webhook-get-by-id get /webhook/{webhookId} Get a webhook endpoint by ID # Overview Source: https://docs.slash.com/api-reference/webhook-overview Use webhooks to receive real-time card and transaction event notifications. ## Setup To begin receiving real-time event notifications via webhooks, visit [Create a webhook](/api-reference/webhook-post) ## Event Ordering Slash cannot guarantee the sequential delivery of events as they occur, nor can it ensure they will be received only once. Most events are generally received in the order in which they occurred; however, events created in close proximity may be received out of order. In the case that an event is received multiple times the `eventId` field should be used as an idempotency key. ## Webhook Retries If Slash does not receive a `2xx` response code from your server within 10 seconds, it will retry delivery up to **12 times** with exponential backoff. ## Endpoint Backoff & Auto-Disable If webhook deliveries consistently fail, Slash will automatically put your endpoint into a `backing-off` state to avoid overwhelming your server. ### How it works 1. **Notification retries**: Each notification is retried up to 12 times before being marked as permanently failed 2. **Endpoint backoff**: After a notification permanently fails (exhausts all retries), the endpoint enters a backoff period 3. **Progressive delays**: Each consecutive permanent failure increases the backoff duration 4. **Auto-disable**: After 6 consecutive permanent failures with no successful deliveries, the endpoint is automatically disabled 5. **Reset on success**: Any successful delivery immediately resets the backoff cycle to zero **Important notes:** * While in `backing-off` or `disabled` status, new events are **queued** (not lost) * When you re-enable the endpoint, all queued events will be delivered * You can re-enable an endpoint at any time via the [Update webhook](/api-reference/webhook-patch) endpoint by setting `status: "active"` * A successful delivery will reset the backoff cycle ## Webhook Signing Every webhook we send includes a `slash-webhook-signature` header. Verify that header against the raw request body using our public RSA keys, [published here](/api-reference/public-rsa-key). We are rotating the signing key. Deliveries use the current key through October 4, 2026, and the new key starting October 5, 2026. During the transition, treat a delivery as authentic when the signature verifies with either key. Verify the exact bytes you received. A parsed and re-serialized body will not match. ### Nodejs Example ```js theme={null} import crypto from 'crypto' const signature = Buffer.from(res.headers['slash-webhook-signature'], 'base64') const body = Buffer.from(res.body) // Both keys are published at /api-reference/public-rsa-key. const keys = [CURRENT_PUBLIC_KEY, NEW_PUBLIC_KEY] const result = keys.some((key) => crypto.verify('sha256', body, key, signature) ) ``` # Update webhook Source: https://docs.slash.com/api-reference/webhook-patch patch /webhook/{webhookId} Update a webhook endpoint's status, enabled events, or both atomically. Provide at least one of `status` or `enabledEvents`. Omitted fields remain unchanged. Set `enabledEvents` to a nonempty array of recognized webhook event names to replace the current selection, or to `null` to receive all events. Omit it to keep the current selection. **Status transitions:** - `active`: Re-enables the endpoint. Use this to: - Unpause a paused endpoint - Re-enable an endpoint that is backing-off or disabled due to delivery failures - When re-enabled, any queued notifications will be sent - `paused`: Pauses the endpoint. While paused: - New notifications are queued (not lost) - No delivery attempts are made - Use `active` to resume and process queued notifications - `archived`: Permanently archives the endpoint (soft delete) **Note:** The `backing-off` and `disabled` statuses are system-managed and cannot be set directly. These occur automatically when delivery failures are detected. Use `active` to re-enable. # Create webhook Source: https://docs.slash.com/api-reference/webhook-post post /webhook Create a new webhook endpoint # Agent Requests Source: https://docs.slash.com/docs/agent-requests ## Overview Agent Requests let AI agents and automated systems make changes through the Slash API with human approval. Write operations are not executed immediately -- they create a pending request that must be approved before taking effect. ## Setup To use agent requests, create a **read-only API key** from the Slash dashboard under **Settings > API Management**. When a read-only API key makes a write request, the operation is automatically deferred for approval instead of being rejected. ## How It Works 1. Your agent makes a write request to any API endpoint using a read-only API key 2. Instead of executing, the API returns a `403` with the request ID in the `x-deferred-action-id` header and an approval URL in the response body 3. An authorized user approves or rejects the request via the approval URL 4. If approved, the original request is executed and the result is available via the API ### Example ```bash theme={null} curl -X POST "https://api.slash.com/card" \ -H "X-API-Key: $API_KEY" \ -H "Content-Type: application/json" \ -d '{"name": "Marketing Card", "type": "virtual"}' ``` Response: ``` HTTP/1.1 403 Forbidden x-deferred-action-id: dfa_abc123 {"error": "Action requires approval: https://app.slash.com/home?modal=/deferred-action/dfa_abc123"} ``` The `x-deferred-action-id` header contains the request ID. The error message includes a URL where an authorized user can review and approve the request. ## Checking Request Status ```bash theme={null} curl "https://api.slash.com/agent-request/dfa_abc123" \ -H "X-API-Key: $API_KEY" ``` ```json theme={null} { "id": "dfa_abc123", "status": "pending", "display": { "title": "Create Virtual Card", "sections": [ { "label": "Card Name", "value": "Marketing Card" }, { "label": "Type", "value": "Virtual" } ] }, "createdAt": "2026-03-05T07:00:00.000Z" } ``` ## Status Values | Status | Description | | - | - | | `pending` | Awaiting approval | | `approved` | Approved, execution starting | | `running` | Currently executing | | `executed` | Successfully executed | | `rejected` | Rejected by approver | | `expired` | Expired before approval | | `failed` | Execution failed | When status is `executed`, the response includes a `result` field containing the response that the original API call would have returned: ```json theme={null} { "id": "dfa_abc123", "status": "executed", "display": { ... }, "result": { "id": "card_xyz789", "name": "Marketing Card", "status": "active" }, "createdAt": "2026-03-05T07:00:00.000Z" } ``` # Idempotent Requests Source: https://docs.slash.com/docs/payments/idempotent-requests Safely retry payment requests without creating duplicates. Network timeouts and dropped connections make it hard to know whether a request that creates a payment intent or refund actually went through. Idempotent requests let you retry safely: send the same request again with the same key and Slash returns the original result instead of creating a second payment intent or refund. ## Using the `X-Idempotency-Key` header Pass an `X-Idempotency-Key` header on [`POST /payments/payment-intent`](/api-reference/payment-intent-post) and [`POST /payments/refund`](/api-reference/refund-post). The key is any string you choose that uniquely identifies the operation on your side — an order id, a refund request id, or a UUID you store alongside the operation. ```bash theme={null} curl https://api.slash.com/payments/payment-intent \ -X POST \ -H "X-API-Key: $SLASH_API_KEY" \ -H "X-Idempotency-Key: order_1234" \ -H "Content-Type: application/json" \ -d '{ "amount": 5000, "currency": "usd", "metadata": { "orderId": "ord_1234" } }' ``` The header is optional. Requests without it are never deduplicated, so a retried request creates a new object every time. ## How keys behave * **Same key, same request** — returns the previously created object. The response is a normal `200`, so your retry logic can treat it exactly like a first-time success. * **Same key, different request body** — rejected. The key is bound to the parameters of the first request; you cannot reuse it to create a different payment intent or refund. * **Scope** — keys are scoped to your legal entity and to the endpoint. The same key string used on `POST /payments/payment-intent` and on `POST /payments/refund` refers to two different operations. # Payment Elements (SDK) Source: https://docs.slash.com/docs/payments/payment-elements Payment Elements is a React SDK that renders the payment-method form for one [payment intent](/api-reference/schema-payment-intent) and confirms it from the browser. Your server creates the intent and hands the `clientSecret` to the page; the SDK does the rest. You keep full control of the surrounding checkout UI, including the email, name, and billing-address fields. Requires React 18 or 19. ```bash theme={null} npm install @slashfi/elements-react ``` ## Overview 1. Your server calls [`POST /payments/payment-intent`](/api-reference/payment-intent-post) and returns the `clientSecret` to the browser. The secret is scoped to that one intent and is only issued on the create response; the SDK uses it both to load the intent and to confirm the payment on the buyer's behalf. 2. Wrap your checkout in ``, render `` where the payment-method form should appear, and render the required ``. 3. Collect email, name, and billing address in your own fields, then call `confirm()` from `usePaymentElements()`. 4. Treat the result as "the buyer is done", not "the payment settled". Fulfill from your server when the intent's `status` becomes `succeeded`, via the [`payments.payment_intent.updated`](/docs/payments/webhooks/payment-intent-updated) webhook. ## Update an intent before confirmation While a payment intent is still `pending`, your server can use [`PATCH /payments/payment-intent/{paymentIntentId}`](/api-reference/payment-intent-patch) to update its amount and/or currency. Updates are rejected after payment processing starts. If the Payment Element is already loaded, call `fetchUpdates()` afterward so the form reflects the new payment details. ## Components ### `` Loads the payment intent for the given `clientSecret` and provides state to everything inside it. Render one provider per payment intent. | Prop | Type | Description | | - | - | - | | `clientSecret` | `string` | **Required.** From the `POST /payments/payment-intent` response. Changing it remounts the provider's children with fresh, isolated state. | | `locale` | `string \| 'auto'` | Optional BCP 47 locale for the payment form (for example `"en"`). `'auto'` follows the browser's language. Unsupported languages fall back to English. | | `children` | `ReactNode` | Your checkout UI. | The provider is safe to render on the server: it emits non-interactive placeholders until it runs in the browser. ### `` The payment-method form. Renders the card input and any wallets enabled for your account, limited to the intent's [`allowedPaymentMethods`](/api-reference/schema-payment-intent#payment-methods) when set. Billing details are **not** collected here; you own those fields and pass them to `confirm()`. The element reports its completeness to the provider, which drives `canConfirm`. ### `` Renders the required payment-processing attribution. **`` is required.** It must be rendered inside the `` on every page that renders ``. Payments from pages that omit it will be blocked. Place it near your pay button. ## React hooks ### `usePaymentElements()` Returns the current `PaymentInitializationState`. Must be called inside a ``. The state is a discriminated union on `state`: | `state` | Fields | Meaning | | - | - | - | | `'loading'` | — | The payment intent is being loaded. Show a spinner and keep the pay button disabled. | | `'error'` | `error: PaymentInitializationError` | The form could not be initialized. See [Initialization errors](#initialization-errors). | | `'ready'` | `canConfirm`, `confirm()`, `fetchUpdates()` | The form is mounted and interactive. | In the `'ready'` state: * **`canConfirm: boolean`** — `true` once the buyer has filled in a complete payment method. Bind your pay button's `disabled` to `!canConfirm`. * **`confirm(input): Promise`** — see [Static methods](#static-methods). * **`fetchUpdates(): Promise`** — see [Static methods](#static-methods). ## Static methods Available on the `'ready'` state returned by `usePaymentElements()`. ### `confirm()` Tokenizes the payment method, confirms the intent, and runs any buyer step (such as 3-D Secure) inline. Resolves to a [`PaymentResult`](#paymentresult) once the buyer is done or the attempt failed. Takes a single object with the buyer details you collected. Missing or malformed fields are rejected before any network call with a `validation_error` listing each offending field. **Call `confirm()` directly from the pay-button click or submit handler.** For wallets like Apple Pay and Google Pay, confirmation can open a wallet sheet that requires live user activation state. Do not perform asynchronous work in the handler before calling `confirm()`, or the wallet may fail to open. Awaiting `confirm()` itself is fine. Buyer's email. Buyer's name as it appears on the card. Billing address for the payment method. Street address. Apartment, suite, or unit. City. State, province, or region. Postal or ZIP code. ISO 3166-1 alpha-2 country code, for example `"US"`. `https://` URL the buyer is sent back to if a payment method requires a full-page redirect. Provide it if you enable wallets or redirect-based methods. ### `fetchUpdates()` Reloads the intent's current amount, currency, and allowed payment methods. Call it after your server changes the intent (for example after applying a discount) so the form reflects the new amount, and after a `payment_intent_updated` error before asking the buyer to confirm again. Overlapping calls share one request. ## Results and errors ### `PaymentResult` `confirm()` never throws for payment outcomes; it resolves to exactly one of: | `type` | Fields | Meaning | | - | - | - | | `'processing'` | `paymentIntentId: string` | The buyer's step is complete (including any redirect handoff). **This is not a settlement guarantee.** Leave the checkout, and let your server fulfill on `status: "succeeded"`. Do not start another attempt on this intent. | | `'error'` | `error: PaymentError` | Confirmation did not complete. Show `error.message` and let the buyer try again where the error allows it (see below). | Repeated `confirm()` calls for the same intent while one is in flight share the same pending promise. ### `PaymentError` | `type` | `code` | Retryable? | Meaning | | - | - | - | - | | `validation_error` | `invalid_customer_details` | Yes | One or more `confirm()` input fields are missing or invalid. `issues[]` lists `{ field, code: 'missing' \| 'invalid', message }` per field, with `field` values such as `email`, `name`, `billingAddress.postalCode`, `returnUrl`. | | `payment_error` | `payment_method_declined` | Yes | The card was declined. `declineCode` may carry the network reason. The intent returns to `pending`; the buyer can try another card. | | `payment_error` | `payment_failed` | Yes | The payment attempt failed for a non-decline reason. | | `payment_error` | `payment_canceled` | Yes | The buyer dismissed the wallet sheet before confirmation completed. This cancels the attempt, not the intent — nothing reaches the server, so the buyer can retry right away. | | `invalid_request_error` | `invalid_client_secret` | No | The `clientSecret` is wrong or no longer valid. Create a new payment intent. | | `invalid_request_error` | `payment_intent_updated` | Yes, after `fetchUpdates()` | The intent changed on the server since the form loaded (for example the amount). Call `fetchUpdates()` and ask the buyer to confirm again. | | `invalid_request_error` | `terminal_payment_intent` | No | The intent is already `succeeded` or `canceled`. | | `api_error` | `unknown` | Check server first | A network or provider error. The attempt may still be locked server-side, so read the intent from your server before retrying. | ### Initialization errors When `usePaymentElements()` returns `state: 'error'`, `error` is a `PaymentInitializationError`: | `type` | `code` | Meaning | | - | - | - | | `invalid_request_error` | `invalid_client_secret` | The `clientSecret` is wrong or expired. | | `invalid_request_error` | `terminal_payment_intent` | The intent is already `succeeded` or `canceled` and cannot be paid. | | `api_error` | `load_failed` | The payment form assets could not be loaded. | | `api_error` | `unknown` | Any other failure while loading the intent. | ## Full example ```tsx theme={null} import { useState } from 'react'; import { BrandingElement, PaymentElement, PaymentProvider, usePaymentElements, type PaymentError, } from '@slashfi/elements-react'; function CheckoutForm() { const payment = usePaymentElements(); const [email, setEmail] = useState(''); const [name, setName] = useState(''); const [postalCode, setPostalCode] = useState(''); const [error, setError] = useState(null); const [submitting, setSubmitting] = useState(false); if (payment.state === 'loading') return

Loading…

; if (payment.state === 'error') return

{payment.error.message}

; async function handlePay() { setSubmitting(true); setError(null); const result = await payment.confirm({ email, name, billingAddress: { line1: '123 Main St', postalCode, country: 'US' }, returnUrl: 'https://merchant.example/checkout/complete', }); setSubmitting(false); if (result.type === 'error') { setError(result.error); return; } // The buyer is done. Your server fulfills when the intent is `succeeded`. window.location.assign(`/orders/pending?pi=${result.paymentIntentId}`); } return ( <> setEmail(e.target.value)} placeholder="Email" /> setName(e.target.value)} placeholder="Name on card" /> setPostalCode(e.target.value)} placeholder="ZIP" /> {error &&

{error.message}

} {/* Required. Payments are blocked if this is not rendered. */} ); } export function Checkout({ clientSecret }: { clientSecret: string }) { return ( ); } ``` ## Gotchas * **`processing` is not success.** Confirmation resolving means the buyer's part is over. Settlement happens asynchronously; the only signal to fulfill on is the intent's `status` reaching `succeeded`, read from your server. Never grant goods or services based on a browser-side result alone. * **Declines are retryable in place.** A `payment_method_declined` result returns the intent to `pending`, so the buyer can enter another card and `confirm()` again without a new intent. * **After an `api_error`, check before retrying.** The attempt may still hold the intent's processing lock. Read the intent from your server; if it is `pending`, the buyer can retry. * **Redirect-based methods.** If the payment method sends the buyer to another page, `confirm()` resolves `processing` at the moment of handoff and the buyer returns to `returnUrl`. Treat that landing page like any other post-checkout page: wait for the webhook. * **One intent per provider.** To switch intents, render a new `` with the new `clientSecret`; state does not carry over. # Quickstart Source: https://docs.slash.com/docs/payments/quickstart Accept your first card payment with the Slash API. Slash payment processing lets you accept card payments on your own site. You create a payment intent server-side with your API key, collect the payment in the browser with [Payment Elements](/docs/payments/payment-elements), and fulfill once the intent settles. Slash Payment Processing is in beta and not enabled for all accounts. Contact [support@joinslash.com](mailto:support@joinslash.com) to get access. ## 1. Create a payment intent Call [`POST /payments/payment-intent`](/api-reference/payment-intent-post) with a legal-entity-scoped API key. Pass a [supported `currency`](/api-reference/payment-intent-post), with `amount` in that currency's smallest unit. Include an `X-Idempotency-Key` so a retried request never creates a duplicate — see [Idempotent requests](/docs/payments/idempotent-requests). ```bash cURL theme={null} curl https://api.slash.com/payments/payment-intent \ -X POST \ -H "X-API-Key: $SLASH_API_KEY" \ -H "X-Idempotency-Key: order_1234" \ -H "Content-Type: application/json" \ -d '{ "amount": 5000, "currency": "usd", "metadata": { "orderId": "ord_1234" } }' ``` ```ts Node.js theme={null} const res = await fetch("https://api.slash.com/payments/payment-intent", { method: "POST", headers: { "X-API-Key": process.env.SLASH_API_KEY, "X-Idempotency-Key": "order_1234", "Content-Type": "application/json", }, body: JSON.stringify({ amount: 5000, currency: "usd", metadata: { orderId: "ord_1234" }, }), }); const { paymentIntent } = await res.json(); const { clientSecret } = paymentIntent; ``` The response includes the payment intent and a `clientSecret`: ```json theme={null} { "paymentIntent": { "id": "acq_payment_intent_...", "status": "pending", "amount": 5000, "currency": "usd", "clientSecret": "...", "metadata": { "orderId": "ord_1234" }, "createdAt": "2026-09-10T00:00:00.000Z", "updatedAt": "2026-09-10T00:00:00.000Z" } } ``` The `clientSecret` is the browser's credential for this one intent: it authorizes loading the payment form *and* confirming the payment, which is what moves the intent from `pending` to `processing`. It grants nothing else. Return it to your frontend, but never log it or store it in browser persistence. ## 2. Collect the payment In the browser, wrap your checkout in `` from [`@slashfi/elements-react`](/docs/payments/payment-elements) with the `clientSecret`, render `` where the card form should go, and call `confirm()` with the buyer's email, name, and billing address when they press pay. ```tsx theme={null} import { BrandingElement, PaymentElement, PaymentProvider, usePaymentElements, } from '@slashfi/elements-react'; function PayButton({ email, name, billingAddress }) { const payment = usePaymentElements(); return ( ); } export function Checkout({ clientSecret }) { return ( ); } ``` A `processing` result means the buyer finished, not that the payment settled. See the [Payment Elements](/docs/payments/payment-elements) reference for the full component, hook, and error surface. ## 3. Fulfill the order Fulfill when the payment intent's `status` becomes `succeeded`. Subscribe to the [`payments.payment_intent.updated`](/docs/payments/webhooks/payment-intent-updated) webhook and read the intent back with [`GET /payments/payment-intent/{id}`](/api-reference/payment-intent-get-by-id) when it fires; if you cannot receive webhooks, poll that endpoint instead. Your `metadata` is returned on every read, so you can correlate the intent to your order without extra lookups. A browser-side success signal can be forged — never grant goods or services on it alone. # Payment Intent Created Source: https://docs.slash.com/docs/payments/webhooks/payment-intent-created webhook payments.payment_intent.created Sent when a payment intent is created via `POST /payments/payment-intent`. Fires once, right after a payment intent is created with [`POST /payments/payment-intent`](/api-reference/payment-intent-post). The intent is `pending` at this point. ## Body The body carries metadata about the event, not the entity itself. Use `entityId` to fetch the current state with [`GET /payments/payment-intent/{paymentIntentId}`](/api-reference/payment-intent-get-by-id), and dedupe on `eventId` since deliveries are at-least-once and unordered. ```json theme={null} { "event": "payments.payment_intent.created", "eventId": "", "entityId": "", "eventTimestamp": "2026-09-10T18:32:30.698Z" } ``` ## Authentication Every delivery is signed with a `slash-webhook-signature` header. Verify it against either of Slash's public keys before trusting the payload; see [Webhook Signing](/api-reference/webhook-overview#webhook-signing) for the verification steps and a code sample. Respond with any `2xx` within 10 seconds to acknowledge; anything else is retried. # Payment Intent Updated Source: https://docs.slash.com/docs/payments/webhooks/payment-intent-updated webhook payments.payment_intent.updated Sent whenever a payment intent changes `status`. Fires every time a payment intent's `status` changes. See the [Payment Intent](/api-reference/schema-payment-intent#statuses) entity for what each status means. ## Body The body carries metadata about the event, not the entity itself. Use `entityId` to fetch the current state with [`GET /payments/payment-intent/{paymentIntentId}`](/api-reference/payment-intent-get-by-id), and dedupe on `eventId` since deliveries are at-least-once and unordered. ```json theme={null} { "event": "payments.payment_intent.updated", "eventId": "", "entityId": "", "eventTimestamp": "2026-09-10T18:32:30.698Z" } ``` ## Authentication Every delivery is signed with a `slash-webhook-signature` header. Verify it against either of Slash's public keys before trusting the payload; see [Webhook Signing](/api-reference/webhook-overview#webhook-signing) for the verification steps and a code sample. Respond with any `2xx` within 10 seconds to acknowledge; anything else is retried. # Refund Created Source: https://docs.slash.com/docs/payments/webhooks/refund-created webhook payments.refund.created Sent when a refund is created via `POST /payments/refund`. Fires once, right after a refund is created with [`POST /payments/refund`](/api-reference/refund-post). The refund is `pending` at this point; no money has moved yet. ## Body The body carries metadata about the event, not the entity itself. Use `entityId` to fetch the current state with [`GET /payments/refund/{refundId}`](/api-reference/refund-get-by-id), and dedupe on `eventId` since deliveries are at-least-once and unordered. ```json theme={null} { "event": "payments.refund.created", "eventId": "", "entityId": "", "eventTimestamp": "2026-09-10T18:32:30.698Z" } ``` ## Authentication Every delivery is signed with a `slash-webhook-signature` header. Verify it against either of Slash's public keys before trusting the payload; see [Webhook Signing](/api-reference/webhook-overview#webhook-signing) for the verification steps and a code sample. Respond with any `2xx` within 10 seconds to acknowledge; anything else is retried. # Refund Updated Source: https://docs.slash.com/docs/payments/webhooks/refund-updated webhook payments.refund.updated Sent whenever a refund changes `status`. Fires every time a refund's `status` changes. See the [Refund](/api-reference/schema-refund#statuses) entity for what each status means. ## Body The body carries metadata about the event, not the entity itself. Use `entityId` to fetch the current state with [`GET /payments/refund/{refundId}`](/api-reference/refund-get-by-id), and dedupe on `eventId` since deliveries are at-least-once and unordered. ```json theme={null} { "event": "payments.refund.updated", "eventId": "", "entityId": "", "eventTimestamp": "2026-09-10T18:32:30.698Z" } ``` ## Authentication Every delivery is signed with a `slash-webhook-signature` header. Verify it against either of Slash's public keys before trusting the payload; see [Webhook Signing](/api-reference/webhook-overview#webhook-signing) for the verification steps and a code sample. Respond with any `2xx` within 10 seconds to acknowledge; anything else is retried.