# 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.
## 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.