Sales Block

Complete commerce infrastructure for subscriptions and payments
The Sales Block provides everything you need to monetize your application - from shopping carts and one-time purchases to subscription billing and payment processing. Integrated with Stripe out of the box.
Features
- Shopping Carts - Persistent carts with line items, discounts, taxes, and inventory validation
- Order Management - Complete order lifecycle from creation to fulfillment
- Subscriptions - Recurring billing with plans, trials, upgrades, and usage-based pricing
- Payment Processing - Secure payments with Stripe, multiple methods, automatic retries
- Invoicing - Automated invoice generation, PDF export, payment reminders
- Checkout Flow - Customizable checkout with guest support, addresses, and tax calculation
API Endpoint
| Service | URL |
|---|---|
| Sales | https://sales.api.us.23blocks.com |
Environment Routing: One URL, one environment. All shared-platform API keys are live keys:
pk_live_*/sk_live_*→ your production tenant. Need a separate staging environment? It is available as a paid add-on on deployments in your own cloud account — see 23blocks in a Box.
Quick Start
Installation
npm install @23blocks/sdk
Initialize the Client
import { create23BlocksClient } from '@23blocks/sdk';
const client = create23BlocksClient({
urls: { sales: 'https://sales.api.us.23blocks.com' },
apiKey: 'your-api-key', // Your live API key (pk_live_*)
});
Create a Shopping Cart
// Create a new cart
const cart = await client.sales.createCart({
userId: 'user-123',
currency: 'USD'
});
// Add items to cart
await client.sales.addCartItem(cart.id, {
productId: 'prod-abc',
quantity: 2,
unitPrice: 29.99
});
// Apply a discount
await client.sales.applyDiscount(cart.id, {
code: 'SAVE20'
});
Create a Subscription
const subscription = await client.sales.createSubscription({
userId: 'user-123',
planId: 'pro-monthly',
paymentMethodId: 'pm_xxx',
trialDays: 14
});
Authentication
All Sales Block endpoints require authentication via API key and App ID headers:
X-App-Id: your-app-id
X-Api-Key: your-api-key
Content-Type: application/json
User-scoped operations also require user authentication:
Authorization: Bearer <user-token>
Shopping Carts
Shopping carts manage the customer's items before checkout, handling inventory validation, pricing, taxes, and discounts.
Cart Object
{
"data": {
"id": "cart-uuid",
"type": "carts",
"attributes": {
"user_id": "user-123",
"status": "active",
"currency": "USD",
"subtotal": 89.97,
"discount_amount": 17.99,
"tax_amount": 5.76,
"total": 77.74,
"item_count": 3,
"metadata": {},
"expires_at": "2026-01-09T12:00:00Z",
"created_at": "2026-01-02T12:00:00Z",
"updated_at": "2026-01-02T12:30:00Z"
},
"relationships": {
"items": {
"data": [
{ "type": "cart_items", "id": "item-1" },
{ "type": "cart_items", "id": "item-2" }
]
},
"discount": {
"data": { "type": "discounts", "id": "discount-1" }
}
}
}
}
Create Cart
POST /api/v1/sales/carts
Request Body:
{
"data": {
"type": "carts",
"attributes": {
"user_id": "user-123",
"currency": "USD",
"metadata": {
"source": "web",
"campaign": "summer-sale"
}
}
}
}
Response: 201 Created
Get Cart
GET /api/v1/sales/carts/:cart_id
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
include | string | Include related resources: items, discount, user |
Response: 200 OK with cart object
Update Cart
PATCH /api/v1/sales/carts/:cart_id
Request Body:
{
"data": {
"type": "carts",
"attributes": {
"currency": "EUR",
"metadata": {
"notes": "Gift order"
}
}
}
}
Delete Cart
DELETE /api/v1/sales/carts/:cart_id
Response: 204 No Content
List User Carts
GET /api/v1/sales/users/:user_id/carts
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
status | string | Filter by status: active, converted, abandoned, expired |
page[number] | integer | Page number (default: 1) |
page[size] | integer | Items per page (default: 20, max: 100) |
Cart Items
Cart Item Object
{
"data": {
"id": "item-uuid",
"type": "cart_items",
"attributes": {
"cart_id": "cart-123",
"product_id": "prod-abc",
"product_name": "Pro Subscription",
"quantity": 1,
"unit_price": 29.99,
"subtotal": 29.99,
"metadata": {},
"created_at": "2026-01-02T12:00:00Z"
}
}
}
Add Item to Cart
POST /api/v1/sales/carts/:cart_id/items
Request Body:
{
"data": {
"type": "cart_items",
"attributes": {
"product_id": "prod-abc",
"quantity": 2,
"unit_price": 29.99,
"metadata": {
"variant": "blue",
"size": "large"
}
}
}
}
Response: 201 Created
Update Cart Item
PATCH /api/v1/sales/carts/:cart_id/items/:item_id
Request Body:
{
"data": {
"type": "cart_items",
"attributes": {
"quantity": 3
}
}
}
Remove Cart Item
DELETE /api/v1/sales/carts/:cart_id/items/:item_id
Response: 204 No Content
Apply Discount to Cart
POST /api/v1/sales/carts/:cart_id/apply-discount
Request Body:
{
"code": "SUMMER20"
}
Response: 200 OK with updated cart
Remove Discount from Cart
DELETE /api/v1/sales/carts/:cart_id/discount
Response: 204 No Content
Orders
Orders represent completed purchases with full lifecycle management.
Order Object
{
"data": {
"id": "order-uuid",
"type": "orders",
"attributes": {
"order_number": "ORD-2026-00001",
"user_id": "user-123",
"status": "confirmed",
"fulfillment_status": "pending",
"payment_status": "paid",
"currency": "USD",
"subtotal": 89.97,
"discount_amount": 17.99,
"shipping_amount": 9.99,
"tax_amount": 5.76,
"total": 87.73,
"billing_address": {
"name": "John Doe",
"line1": "123 Main St",
"city": "San Francisco",
"state": "CA",
"postal_code": "94102",
"country": "US"
},
"shipping_address": {
"name": "John Doe",
"line1": "123 Main St",
"city": "San Francisco",
"state": "CA",
"postal_code": "94102",
"country": "US"
},
"metadata": {},
"notes": "Leave at door",
"created_at": "2026-01-02T12:00:00Z",
"updated_at": "2026-01-02T12:00:00Z"
},
"relationships": {
"line_items": {
"data": [
{ "type": "order_items", "id": "item-1" }
]
},
"payment": {
"data": { "type": "payments", "id": "pay-1" }
},
"invoice": {
"data": { "type": "invoices", "id": "inv-1" }
}
}
}
}
Order Statuses
| Status | Description |
|---|---|
pending | Order created, awaiting payment |
confirmed | Payment received, order confirmed |
processing | Order is being processed |
shipped | Order has been shipped |
delivered | Order delivered to customer |
cancelled | Order was cancelled |
refunded | Order was refunded |
Create Order
POST /api/v1/sales/orders
Request Body:
{
"data": {
"type": "orders",
"attributes": {
"user_id": "user-123",
"cart_id": "cart-456",
"payment_method_id": "pm_xxx",
"billing_address": {
"name": "John Doe",
"line1": "123 Main St",
"city": "San Francisco",
"state": "CA",
"postal_code": "94102",
"country": "US"
},
"shipping_address": {
"name": "John Doe",
"line1": "123 Main St",
"city": "San Francisco",
"state": "CA",
"postal_code": "94102",
"country": "US"
},
"notes": "Leave at door"
}
}
}
Response: 201 Created
Get Order
GET /api/v1/sales/orders/:order_id
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
include | string | Include: line_items, payment, invoice, user |
List Orders
GET /api/v1/sales/orders
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
filter[user_id] | string | Filter by user ID |
filter[status] | string | Filter by status |
filter[payment_status] | string | Filter: pending, paid, failed, refunded |
filter[fulfillment_status] | string | Filter: pending, processing, shipped, delivered |
filter[created_after] | datetime | Orders created after date |
filter[created_before] | datetime | Orders created before date |
sort | string | Sort by: created_at, -created_at, total, -total |
page[number] | integer | Page number |
page[size] | integer | Items per page |
Update Order Status
PATCH /api/v1/sales/orders/:order_id/status
Request Body:
{
"status": "shipped",
"tracking_number": "1Z999AA10123456784",
"carrier": "UPS"
}
Cancel Order
POST /api/v1/sales/orders/:order_id/cancel
Request Body:
{
"reason": "Customer requested cancellation"
}
Subscriptions
Manage recurring billing with flexible subscription plans.
Subscription Object
{
"data": {
"id": "sub-uuid",
"type": "subscriptions",
"attributes": {
"user_id": "user-123",
"plan_id": "plan-pro",
"status": "active",
"current_period_start": "2026-01-02T00:00:00Z",
"current_period_end": "2026-02-02T00:00:00Z",
"trial_start": "2025-12-19T00:00:00Z",
"trial_end": "2026-01-02T00:00:00Z",
"cancel_at_period_end": false,
"cancelled_at": null,
"ended_at": null,
"quantity": 1,
"metadata": {},
"created_at": "2025-12-19T12:00:00Z",
"updated_at": "2026-01-02T12:00:00Z"
},
"relationships": {
"plan": {
"data": { "type": "plans", "id": "plan-pro" }
},
"payment_method": {
"data": { "type": "payment_methods", "id": "pm-1" }
}
}
}
}
Subscription Statuses
| Status | Description |
|---|---|
trialing | Subscription is in trial period |
active | Subscription is active and billing |
past_due | Payment failed, retrying |
paused | Subscription temporarily paused |
cancelled | Subscription cancelled |
unpaid | Payment failed after all retries |
Create Subscription
POST /api/v1/sales/subscriptions
Request Body:
{
"data": {
"type": "subscriptions",
"attributes": {
"user_id": "user-123",
"plan_id": "plan-pro",
"payment_method_id": "pm_xxx",
"trial_days": 14,
"quantity": 5,
"metadata": {
"team_name": "Engineering"
}
}
}
}
Response: 201 Created
Get Subscription
GET /api/v1/sales/subscriptions/:subscription_id
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
include | string | Include: plan, payment_method, invoices, user |
List Subscriptions
GET /api/v1/sales/subscriptions
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
filter[user_id] | string | Filter by user |
filter[plan_id] | string | Filter by plan |
filter[status] | string | Filter by status |
page[number] | integer | Page number |
page[size] | integer | Items per page |
Update Subscription
PATCH /api/v1/sales/subscriptions/:subscription_id
Request Body:
{
"data": {
"type": "subscriptions",
"attributes": {
"plan_id": "plan-enterprise",
"quantity": 10,
"proration_behavior": "create_prorations"
}
}
}
Proration Behaviors:
| Value | Description |
|---|---|
create_prorations | Credit unused time, charge for new plan |
none | No proration, change at next billing cycle |
always_invoice | Invoice immediately for the change |
Pause Subscription
POST /api/v1/sales/subscriptions/:subscription_id/pause
Request Body:
{
"resume_at": "2026-03-01T00:00:00Z"
}
Resume Subscription
POST /api/v1/sales/subscriptions/:subscription_id/resume
Cancel Subscription
POST /api/v1/sales/subscriptions/:subscription_id/cancel
Request Body:
{
"cancel_at_period_end": true,
"reason": "Too expensive",
"feedback": "Will reconsider when budget allows"
}
Reactivate Subscription
Reactivate a subscription that was scheduled for cancellation:
POST /api/v1/sales/subscriptions/:subscription_id/reactivate
Subscription Plans
Define your pricing tiers and billing configurations.
Plan Object
{
"data": {
"id": "plan-uuid",
"type": "plans",
"attributes": {
"name": "Pro Plan",
"description": "For growing teams",
"product_id": "prod-123",
"currency": "USD",
"amount": 4999,
"interval": "month",
"interval_count": 1,
"trial_period_days": 14,
"usage_type": "licensed",
"billing_scheme": "per_unit",
"features": [
"Unlimited projects",
"Priority support",
"Advanced analytics"
],
"metadata": {},
"active": true,
"created_at": "2026-01-01T00:00:00Z"
}
}
}
Billing Intervals
| Interval | Description |
|---|---|
day | Daily billing |
week | Weekly billing |
month | Monthly billing |
year | Annual billing |
Usage Types
| Type | Description |
|---|---|
licensed | Fixed quantity billing (per seat) |
metered | Usage-based billing |
Create Plan
POST /api/v1/sales/plans
Request Body:
{
"data": {
"type": "plans",
"attributes": {
"name": "Enterprise",
"description": "For large organizations",
"product_id": "prod-123",
"currency": "USD",
"amount": 19999,
"interval": "month",
"interval_count": 1,
"trial_period_days": 30,
"usage_type": "licensed",
"features": [
"Unlimited everything",
"24/7 support",
"Custom integrations",
"SLA guarantee"
]
}
}
}
List Plans
GET /api/v1/sales/plans
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
filter[active] | boolean | Filter active/inactive plans |
filter[product_id] | string | Filter by product |
filter[interval] | string | Filter by billing interval |
Update Plan
PATCH /api/v1/sales/plans/:plan_id
Updating a plan's price does not affect existing subscriptions. Create a new plan for new pricing.
Archive Plan
POST /api/v1/sales/plans/:plan_id/archive
Usage Records (Metered Billing)
Track usage for metered subscriptions.
Usage Record Object
{
"data": {
"id": "usage-uuid",
"type": "usage_records",
"attributes": {
"subscription_id": "sub-123",
"quantity": 1500,
"action": "increment",
"timestamp": "2026-01-02T12:00:00Z",
"metadata": {
"api_calls": 1000,
"storage_gb": 5
}
}
}
}
Report Usage
POST /api/v1/sales/subscriptions/:subscription_id/usage
Request Body:
{
"data": {
"type": "usage_records",
"attributes": {
"quantity": 500,
"action": "increment",
"timestamp": "2026-01-02T12:00:00Z",
"metadata": {
"feature": "api_calls"
}
}
}
}
Actions:
| Action | Description |
|---|---|
increment | Add to current usage |
set | Override current usage |
Get Usage Summary
GET /api/v1/sales/subscriptions/:subscription_id/usage/summary
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
period_start | datetime | Start of period |
period_end | datetime | End of period |
Payments
Process payments securely with Stripe integration.
Payment Object
{
"data": {
"id": "pay-uuid",
"type": "payments",
"attributes": {
"amount": 4999,
"currency": "USD",
"status": "succeeded",
"payment_method_type": "card",
"card_brand": "visa",
"card_last4": "4242",
"stripe_payment_intent_id": "pi_xxx",
"order_id": "order-123",
"subscription_id": null,
"invoice_id": "inv-123",
"failure_code": null,
"failure_message": null,
"metadata": {},
"created_at": "2026-01-02T12:00:00Z"
}
}
}
Payment Statuses
| Status | Description |
|---|---|
pending | Payment initiated |
processing | Payment being processed |
succeeded | Payment successful |
failed | Payment failed |
cancelled | Payment cancelled |
refunded | Payment refunded |
partially_refunded | Partial refund issued |
Create Payment
POST /api/v1/sales/payments
Request Body:
{
"data": {
"type": "payments",
"attributes": {
"amount": 9999,
"currency": "USD",
"payment_method_id": "pm_xxx",
"order_id": "order-123",
"metadata": {
"description": "Order payment"
}
}
}
}
Get Payment
GET /api/v1/sales/payments/:payment_id
List Payments
GET /api/v1/sales/payments
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
filter[user_id] | string | Filter by user |
filter[status] | string | Filter by status |
filter[order_id] | string | Filter by order |
filter[subscription_id] | string | Filter by subscription |
filter[created_after] | datetime | Payments after date |
filter[created_before] | datetime | Payments before date |
sort | string | Sort field |
page[number] | integer | Page number |
page[size] | integer | Items per page |
Refund Payment
POST /api/v1/sales/payments/:payment_id/refund
Request Body:
{
"amount": 2500,
"reason": "customer_request",
"metadata": {
"refund_reason": "Product not as described"
}
}
Refund Reasons:
| Reason | Description |
|---|---|
duplicate | Duplicate charge |
fraudulent | Fraudulent transaction |
customer_request | Customer requested refund |
Payment Methods
Manage customer payment methods.
Payment Method Object
{
"data": {
"id": "pm-uuid",
"type": "payment_methods",
"attributes": {
"user_id": "user-123",
"type": "card",
"card": {
"brand": "visa",
"last4": "4242",
"exp_month": 12,
"exp_year": 2028,
"funding": "credit"
},
"billing_details": {
"name": "John Doe",
"email": "john@example.com"
},
"is_default": true,
"stripe_payment_method_id": "pm_xxx",
"created_at": "2026-01-02T12:00:00Z"
}
}
}
Add Payment Method
POST /api/v1/sales/payment-methods
Request Body:
{
"data": {
"type": "payment_methods",
"attributes": {
"user_id": "user-123",
"stripe_payment_method_id": "pm_xxx",
"set_as_default": true
}
}
}
List User Payment Methods
GET /api/v1/sales/users/:user_id/payment-methods
Set Default Payment Method
POST /api/v1/sales/payment-methods/:payment_method_id/set-default
Delete Payment Method
DELETE /api/v1/sales/payment-methods/:payment_method_id
Invoices
Automated invoice generation and management.
Invoice Object
{
"data": {
"id": "inv-uuid",
"type": "invoices",
"attributes": {
"invoice_number": "INV-2026-00001",
"user_id": "user-123",
"subscription_id": "sub-123",
"status": "paid",
"currency": "USD",
"subtotal": 4999,
"discount_amount": 0,
"tax_amount": 400,
"total": 5399,
"amount_paid": 5399,
"amount_due": 0,
"billing_reason": "subscription_cycle",
"period_start": "2026-01-02T00:00:00Z",
"period_end": "2026-02-02T00:00:00Z",
"due_date": "2026-01-16T00:00:00Z",
"paid_at": "2026-01-02T12:00:00Z",
"pdf_url": "https://api.23blocks.com/invoices/inv-uuid.pdf",
"hosted_invoice_url": "https://pay.23blocks.com/inv-uuid",
"metadata": {},
"created_at": "2026-01-02T00:00:00Z"
},
"relationships": {
"line_items": {
"data": [
{ "type": "invoice_items", "id": "ii-1" }
]
},
"payment": {
"data": { "type": "payments", "id": "pay-1" }
}
}
}
}
Invoice Statuses
| Status | Description |
|---|---|
draft | Invoice being prepared |
open | Invoice sent, awaiting payment |
paid | Invoice paid in full |
void | Invoice voided |
uncollectible | Invoice marked uncollectible |
Get Invoice
GET /api/v1/sales/invoices/:invoice_id
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
include | string | Include: line_items, payment, subscription |
List Invoices
GET /api/v1/sales/invoices
Query Parameters:
| Parameter | Type | Description |
|---|---|---|
filter[user_id] | string | Filter by user |
filter[subscription_id] | string | Filter by subscription |
filter[status] | string | Filter by status |
filter[due_after] | datetime | Invoices due after date |
filter[due_before] | datetime | Invoices due before date |
sort | string | Sort field |
page[number] | integer | Page number |
page[size] | integer | Items per page |
Download Invoice PDF
GET /api/v1/sales/invoices/:invoice_id/pdf
Response: PDF file download
Send Invoice
POST /api/v1/sales/invoices/:invoice_id/send
Sends the invoice to the customer via email.
Void Invoice
POST /api/v1/sales/invoices/:invoice_id/void
Create Manual Invoice
POST /api/v1/sales/invoices
Request Body:
{
"data": {
"type": "invoices",
"attributes": {
"user_id": "user-123",
"due_date": "2026-01-16T00:00:00Z",
"line_items": [
{
"description": "Consulting services",
"quantity": 10,
"unit_price": 15000
},
{
"description": "Setup fee",
"quantity": 1,
"unit_price": 50000
}
],
"metadata": {
"project": "Website redesign"
}
}
}
}
Discounts & Coupons
Create promotional codes and discounts.
Discount Object
{
"data": {
"id": "disc-uuid",
"type": "discounts",
"attributes": {
"code": "SUMMER20",
"name": "Summer Sale 20% Off",
"type": "percentage",
"value": 20,
"currency": "USD",
"max_redemptions": 1000,
"redemptions_count": 456,
"applies_to": "all",
"minimum_amount": 5000,
"valid_from": "2026-06-01T00:00:00Z",
"valid_until": "2026-08-31T23:59:59Z",
"active": true,
"created_at": "2026-05-01T00:00:00Z"
}
}
}
Discount Types
| Type | Description |
|---|---|
percentage | Percentage off (e.g., 20% off) |
fixed_amount | Fixed amount off (e.g., $10 off) |
Create Discount
POST /api/v1/sales/discounts
Request Body:
{
"data": {
"type": "discounts",
"attributes": {
"code": "WELCOME10",
"name": "Welcome Discount",
"type": "percentage",
"value": 10,
"max_redemptions": 500,
"applies_to": "first_order",
"minimum_amount": 2500,
"valid_until": "2026-12-31T23:59:59Z"
}
}
}
Validate Discount Code
POST /api/v1/sales/discounts/validate
Request Body:
{
"code": "SUMMER20",
"cart_id": "cart-123",
"user_id": "user-123"
}
Response:
{
"valid": true,
"discount": {
"type": "percentage",
"value": 20,
"calculated_amount": 1799
}
}
List Discounts
GET /api/v1/sales/discounts
Deactivate Discount
POST /api/v1/sales/discounts/:discount_id/deactivate
Checkout
Complete checkout flow from cart to order.
Create Checkout Session
POST /api/v1/sales/checkout/sessions
Request Body:
{
"data": {
"type": "checkout_sessions",
"attributes": {
"cart_id": "cart-123",
"success_url": "https://yourapp.com/success?session={session_id}",
"cancel_url": "https://yourapp.com/cart",
"mode": "payment",
"allow_guest_checkout": true,
"collect_shipping_address": true,
"shipping_options": [
{
"id": "standard",
"name": "Standard Shipping",
"amount": 999,
"delivery_estimate": "5-7 business days"
},
{
"id": "express",
"name": "Express Shipping",
"amount": 2499,
"delivery_estimate": "2-3 business days"
}
]
}
}
}
Checkout Modes:
| Mode | Description |
|---|---|
payment | One-time payment |
subscription | Recurring subscription |
setup | Setup payment method only |
Response:
{
"data": {
"id": "cs-uuid",
"type": "checkout_sessions",
"attributes": {
"url": "https://checkout.23blocks.com/cs-uuid",
"expires_at": "2026-01-02T13:00:00Z",
"status": "open"
}
}
}
Get Checkout Session
GET /api/v1/sales/checkout/sessions/:session_id
Complete Checkout (API)
For custom checkout UIs:
POST /api/v1/sales/checkout/complete
Request Body:
{
"data": {
"type": "checkout",
"attributes": {
"cart_id": "cart-123",
"payment_method_id": "pm_xxx",
"billing_address": {
"name": "John Doe",
"line1": "123 Main St",
"city": "San Francisco",
"state": "CA",
"postal_code": "94102",
"country": "US"
},
"shipping_address": {
"name": "John Doe",
"line1": "123 Main St",
"city": "San Francisco",
"state": "CA",
"postal_code": "94102",
"country": "US"
},
"shipping_option_id": "express",
"discount_code": "SAVE20",
"email": "john@example.com"
}
}
}
Response: 201 Created with order object
Calculate Taxes
POST /api/v1/sales/checkout/calculate-taxes
Request Body:
{
"cart_id": "cart-123",
"shipping_address": {
"postal_code": "94102",
"country": "US",
"state": "CA"
}
}
Response:
{
"subtotal": 8997,
"tax_amount": 742,
"tax_rate": 8.25,
"tax_breakdown": [
{ "name": "State Tax", "rate": 6.0, "amount": 540 },
{ "name": "County Tax", "rate": 1.25, "amount": 112 },
{ "name": "City Tax", "rate": 1.0, "amount": 90 }
],
"total": 9739
}
Webhooks
Receive real-time notifications for sales events.
Available Events
| Event | Description |
|---|---|
cart.created | New cart created |
cart.updated | Cart modified |
cart.abandoned | Cart abandoned (no activity) |
order.created | New order placed |
order.paid | Order payment received |
order.shipped | Order shipped |
order.delivered | Order delivered |
order.cancelled | Order cancelled |
order.refunded | Order refunded |
subscription.created | New subscription created |
subscription.updated | Subscription modified |
subscription.trial_ending | Trial ending in 3 days |
subscription.cancelled | Subscription cancelled |
subscription.paused | Subscription paused |
subscription.resumed | Subscription resumed |
payment.succeeded | Payment successful |
payment.failed | Payment failed |
payment.refunded | Payment refunded |
invoice.created | Invoice generated |
invoice.paid | Invoice paid |
invoice.payment_failed | Invoice payment failed |
invoice.upcoming | Invoice will be sent soon |
Webhook Payload
{
"id": "evt-uuid",
"type": "subscription.created",
"created_at": "2026-01-02T12:00:00Z",
"data": {
"id": "sub-123",
"type": "subscriptions",
"attributes": {
"user_id": "user-123",
"plan_id": "plan-pro",
"status": "trialing"
}
}
}
Configure Webhooks
POST /api/v1/sales/webhooks
Request Body:
{
"data": {
"type": "webhooks",
"attributes": {
"url": "https://yourapp.com/webhooks/sales",
"events": [
"subscription.created",
"subscription.cancelled",
"payment.succeeded",
"payment.failed"
],
"secret": "whsec_xxx"
}
}
}
Verify Webhook Signature
import { verifyWebhookSignature } from '@23blocks/sdk';
const isValid = verifyWebhookSignature(
payload,
signature,
webhookSecret
);
SDK Examples
Complete E-commerce Flow
import { create23BlocksClient } from '@23blocks/sdk';
const client = create23BlocksClient({
urls: { sales: 'https://sales.api.us.23blocks.com' },
appId: 'your-app-id',
apiKey: 'your-api-key',
});
// 1. Create cart and add items
const cart = await client.sales.createCart({
userId: 'user-123',
currency: 'USD'
});
await client.sales.addCartItem(cart.id, {
productId: 'prod-shirt',
quantity: 2,
unitPrice: 29.99,
metadata: { size: 'L', color: 'blue' }
});
await client.sales.addCartItem(cart.id, {
productId: 'prod-hat',
quantity: 1,
unitPrice: 19.99
});
// 2. Apply discount
await client.sales.applyDiscount(cart.id, {
code: 'SAVE10'
});
// 3. Calculate taxes
const taxes = await client.sales.calculateTaxes(cart.id, {
shippingAddress: {
postalCode: '94102',
country: 'US',
state: 'CA'
}
});
// 4. Create checkout session
const session = await client.sales.createCheckoutSession({
cartId: cart.id,
successUrl: 'https://yourapp.com/success',
cancelUrl: 'https://yourapp.com/cart',
collectShippingAddress: true
});
// Redirect customer to session.url
Subscription Management
// Create subscription with trial
const subscription = await client.sales.createSubscription({
userId: 'user-123',
planId: 'plan-pro-monthly',
paymentMethodId: 'pm_xxx',
trialDays: 14,
metadata: { referral: 'partner-123' }
});
// Upgrade subscription
await client.sales.updateSubscription(subscription.id, {
planId: 'plan-enterprise-monthly',
prorationBehavior: 'create_prorations'
});
// Add seats
await client.sales.updateSubscription(subscription.id, {
quantity: 10
});
// Cancel at period end
await client.sales.cancelSubscription(subscription.id, {
cancelAtPeriodEnd: true,
reason: 'Switching to competitor'
});
// Reactivate before period ends
await client.sales.reactivateSubscription(subscription.id);
Usage-Based Billing
// Report API usage
await client.sales.reportUsage(subscription.id, {
quantity: 1500,
action: 'increment',
timestamp: new Date().toISOString(),
metadata: { feature: 'api_calls' }
});
// Get usage summary
const usage = await client.sales.getUsageSummary(subscription.id, {
periodStart: '2026-01-01T00:00:00Z',
periodEnd: '2026-01-31T23:59:59Z'
});
console.log(`Total API calls: ${usage.total}`);
Invoice Management
// List user invoices
const invoices = await client.sales.listInvoices({
filter: { userId: 'user-123', status: 'paid' },
sort: '-created_at',
page: { number: 1, size: 10 }
});
// Download invoice PDF
const pdfBlob = await client.sales.downloadInvoicePdf('inv-123');
// Create manual invoice
const invoice = await client.sales.createInvoice({
userId: 'user-123',
dueDate: '2026-01-16T00:00:00Z',
lineItems: [
{ description: 'Consulting', quantity: 8, unitPrice: 15000 },
{ description: 'Setup', quantity: 1, unitPrice: 25000 }
]
});
// Send invoice
await client.sales.sendInvoice(invoice.id);
Payment Processing
// Add payment method
const paymentMethod = await client.sales.addPaymentMethod({
userId: 'user-123',
stripePaymentMethodId: 'pm_xxx',
setAsDefault: true
});
// Process one-time payment
const payment = await client.sales.createPayment({
amount: 9999,
currency: 'USD',
paymentMethodId: paymentMethod.id,
metadata: { description: 'Custom service' }
});
// Refund payment
await client.sales.refundPayment(payment.id, {
amount: 5000, // Partial refund
reason: 'customer_request'
});
Error Handling
Error Response Format
{
"errors": [
{
"status": "422",
"code": "insufficient_funds",
"title": "Payment Failed",
"detail": "The card has insufficient funds to complete this transaction.",
"meta": {
"decline_code": "insufficient_funds",
"payment_intent_id": "pi_xxx"
}
}
]
}
Common Error Codes
| Code | HTTP Status | Description |
|---|---|---|
cart_not_found | 404 | Cart does not exist |
cart_expired | 410 | Cart has expired |
cart_empty | 422 | Cannot checkout empty cart |
invalid_quantity | 422 | Quantity must be positive |
insufficient_inventory | 422 | Product out of stock |
invalid_discount | 422 | Discount code invalid or expired |
discount_minimum_not_met | 422 | Order doesn't meet minimum for discount |
payment_failed | 422 | Payment could not be processed |
card_declined | 422 | Card was declined |
insufficient_funds | 422 | Card has insufficient funds |
subscription_not_found | 404 | Subscription does not exist |
plan_not_found | 404 | Plan does not exist |
plan_inactive | 422 | Plan is no longer available |
subscription_cancelled | 422 | Cannot modify cancelled subscription |
invoice_already_paid | 422 | Invoice has already been paid |
Rate Limits
| Endpoint | Limit |
|---|---|
| Cart operations | 100 requests/minute |
| Order creation | 30 requests/minute |
| Payment processing | 50 requests/minute |
| Subscription operations | 60 requests/minute |
| Usage reporting | 300 requests/minute |
| Webhook endpoints | 1000 events/minute |
Best Practices
Cart Management
- Set appropriate cart expiration times (default: 7 days)
- Clear abandoned carts periodically
- Validate inventory before checkout
Subscription Billing
- Always offer trial periods for new customers
- Use proration for plan changes
- Send reminder emails before trial ends
- Handle failed payments with retry logic
Payment Security
- Never store full card numbers
- Use Stripe's tokenization
- Verify webhook signatures
- Implement idempotency keys for payments
Checkout Optimization
- Minimize checkout steps
- Offer guest checkout
- Pre-fill known customer data
- Show clear pricing breakdown
Related Resources
- Products Block - Product catalog management
- Auth Block - User authentication
- Frontend SDK - Client-side integration
Support
- Documentation: 23blocks.com/developers/blocks/sales
- API Reference: api.23blocks.com/docs
- Support: 23blocks.com/support
- Status: heartbeat.23blocks.com
Use This Block with AI Agents
This block ships an official Claude Code plugin from the public 23blocks marketplace (19 plugins, 205 skills). Your agent gets full working knowledge of this API — endpoints, identity registration, and conventions.
# one-time: add the 23blocks marketplace
claude plugin marketplace add https://github.com/23blocks-OS/ai-agents
# install this block's plugin
claude plugin install sales-block
Configure BLOCKS_API_URL, BLOCKS_API_KEY, and BLOCKS_AUTH_TOKEN. Register identity first: each block is autonomous — register the user with this block's identity endpoint before private calls (only user_unique_id is required; email/phone are optional notification-routing fields).
Full reference: AI Agents & Claude Code Plugins · github.com/23blocks-OS/ai-agents