Skip to main content

Sales Block

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

ServiceURL
Saleshttps://sales.api.us.23blocks.com

Environment Routing: Use the same URL for all environments. Your API key determines the environment:

  • pk_test_* / sk_test_* → Routes to Staging
  • pk_live_* / sk_live_* → Routes to Production

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', // Use pk_test_* for staging, pk_live_* for production
});

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:

ParameterTypeDescription
includestringInclude 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:

ParameterTypeDescription
statusstringFilter by status: active, converted, abandoned, expired
page[number]integerPage number (default: 1)
page[size]integerItems 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

StatusDescription
pendingOrder created, awaiting payment
confirmedPayment received, order confirmed
processingOrder is being processed
shippedOrder has been shipped
deliveredOrder delivered to customer
cancelledOrder was cancelled
refundedOrder 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:

ParameterTypeDescription
includestringInclude: line_items, payment, invoice, user

List Orders

GET /api/v1/sales/orders

Query Parameters:

ParameterTypeDescription
filter[user_id]stringFilter by user ID
filter[status]stringFilter by status
filter[payment_status]stringFilter: pending, paid, failed, refunded
filter[fulfillment_status]stringFilter: pending, processing, shipped, delivered
filter[created_after]datetimeOrders created after date
filter[created_before]datetimeOrders created before date
sortstringSort by: created_at, -created_at, total, -total
page[number]integerPage number
page[size]integerItems 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

StatusDescription
trialingSubscription is in trial period
activeSubscription is active and billing
past_duePayment failed, retrying
pausedSubscription temporarily paused
cancelledSubscription cancelled
unpaidPayment 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:

ParameterTypeDescription
includestringInclude: plan, payment_method, invoices, user

List Subscriptions

GET /api/v1/sales/subscriptions

Query Parameters:

ParameterTypeDescription
filter[user_id]stringFilter by user
filter[plan_id]stringFilter by plan
filter[status]stringFilter by status
page[number]integerPage number
page[size]integerItems 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:

ValueDescription
create_prorationsCredit unused time, charge for new plan
noneNo proration, change at next billing cycle
always_invoiceInvoice 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

IntervalDescription
dayDaily billing
weekWeekly billing
monthMonthly billing
yearAnnual billing

Usage Types

TypeDescription
licensedFixed quantity billing (per seat)
meteredUsage-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:

ParameterTypeDescription
filter[active]booleanFilter active/inactive plans
filter[product_id]stringFilter by product
filter[interval]stringFilter by billing interval

Update Plan

PATCH /api/v1/sales/plans/:plan_id
caution

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:

ActionDescription
incrementAdd to current usage
setOverride current usage

Get Usage Summary

GET /api/v1/sales/subscriptions/:subscription_id/usage/summary

Query Parameters:

ParameterTypeDescription
period_startdatetimeStart of period
period_enddatetimeEnd 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

StatusDescription
pendingPayment initiated
processingPayment being processed
succeededPayment successful
failedPayment failed
cancelledPayment cancelled
refundedPayment refunded
partially_refundedPartial 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:

ParameterTypeDescription
filter[user_id]stringFilter by user
filter[status]stringFilter by status
filter[order_id]stringFilter by order
filter[subscription_id]stringFilter by subscription
filter[created_after]datetimePayments after date
filter[created_before]datetimePayments before date
sortstringSort field
page[number]integerPage number
page[size]integerItems 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:

ReasonDescription
duplicateDuplicate charge
fraudulentFraudulent transaction
customer_requestCustomer 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

StatusDescription
draftInvoice being prepared
openInvoice sent, awaiting payment
paidInvoice paid in full
voidInvoice voided
uncollectibleInvoice marked uncollectible

Get Invoice

GET /api/v1/sales/invoices/:invoice_id

Query Parameters:

ParameterTypeDescription
includestringInclude: line_items, payment, subscription

List Invoices

GET /api/v1/sales/invoices

Query Parameters:

ParameterTypeDescription
filter[user_id]stringFilter by user
filter[subscription_id]stringFilter by subscription
filter[status]stringFilter by status
filter[due_after]datetimeInvoices due after date
filter[due_before]datetimeInvoices due before date
sortstringSort field
page[number]integerPage number
page[size]integerItems 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

TypeDescription
percentagePercentage off (e.g., 20% off)
fixed_amountFixed 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:

ModeDescription
paymentOne-time payment
subscriptionRecurring subscription
setupSetup 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

EventDescription
cart.createdNew cart created
cart.updatedCart modified
cart.abandonedCart abandoned (no activity)
order.createdNew order placed
order.paidOrder payment received
order.shippedOrder shipped
order.deliveredOrder delivered
order.cancelledOrder cancelled
order.refundedOrder refunded
subscription.createdNew subscription created
subscription.updatedSubscription modified
subscription.trial_endingTrial ending in 3 days
subscription.cancelledSubscription cancelled
subscription.pausedSubscription paused
subscription.resumedSubscription resumed
payment.succeededPayment successful
payment.failedPayment failed
payment.refundedPayment refunded
invoice.createdInvoice generated
invoice.paidInvoice paid
invoice.payment_failedInvoice payment failed
invoice.upcomingInvoice 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

CodeHTTP StatusDescription
cart_not_found404Cart does not exist
cart_expired410Cart has expired
cart_empty422Cannot checkout empty cart
invalid_quantity422Quantity must be positive
insufficient_inventory422Product out of stock
invalid_discount422Discount code invalid or expired
discount_minimum_not_met422Order doesn't meet minimum for discount
payment_failed422Payment could not be processed
card_declined422Card was declined
insufficient_funds422Card has insufficient funds
subscription_not_found404Subscription does not exist
plan_not_found404Plan does not exist
plan_inactive422Plan is no longer available
subscription_cancelled422Cannot modify cancelled subscription
invoice_already_paid422Invoice has already been paid

Rate Limits

EndpointLimit
Cart operations100 requests/minute
Order creation30 requests/minute
Payment processing50 requests/minute
Subscription operations60 requests/minute
Usage reporting300 requests/minute
Webhook endpoints1000 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


Support