Payment Sessions

Overview

Create hosted checkout pages for your customers

Payment Sessions provide a hosted checkout experience for collecting payments. Create a session, send the checkout URL to your customer, and they complete payment on Snippe's secure checkout page.

Sessions are ideal when you want a pre-built, mobile-optimized checkout UI without building your own payment form.


How It Works

Create Session

Call the API with amount and configuration to get a checkout URL.

Share Checkout URL

Send the checkout_url to your customer via email, SMS, WhatsApp, or embed it in your app.

Customer Pays

Customer opens the link, chooses a payment method, and completes payment on Snippe's secure checkout page.

Receive Webhook

You receive a payment.completed webhook when payment succeeds.


API Endpoints

Merchant Endpoints

All merchant endpoints require authentication via Authorization: Bearer <token> (JWT) or X-API-Key: <api_key>.

EndpointMethodDescription
/api/v1/sessionsPOSTCreate session
/api/v1/sessionsGETList sessions
/api/v1/sessions/:referenceGETGet session details
/api/v1/sessions/:reference/cancelPOSTCancel session

Public Endpoints

These endpoints power the hosted checkout page and do not require authentication.

EndpointMethodDescription
/checkout/:tokenGETLoad checkout page
/checkout/:token/payPOSTSubmit payment
/checkout/:token/statusGETPoll payment status
/checkout/:token/cancel/:attemptIDPOSTCancel payment attempt
/p/:codeGETShort link redirect

Create Session

POST /api/v1/sessions
Authorization: Bearer <token>  # or X-API-Key: <api_key>
Content-Type: application/json

Request Body

FieldTypeRequiredDefaultNotes
amountuint64Yes (fixed) / No (custom)Min 500. Suggested amount when allow_custom_amount is true
currencystringNoTZSISO 4217
allowed_methodsstring[]No["mobile_money", "qr"]mobile_money, qr, card
allow_custom_amountboolNofalseCustomer enters own amount at checkout
min_amountuint64Yes (when custom)Min 500, must be < max_amount
max_amountuint64Yes (when custom)Must be > min_amount
customerobjectNoPre-fills checkout form
profile_idstringNoUser's defaultPayment profile for branding
redirect_urlstringNoWhere to send customer after payment. Max 500 chars
webhook_urlstringNoReceives payment events. Max 500 chars
descriptionstringNoMax 500 chars
metadataobjectNoMax 50 keys, custom key-value data
expires_inintNo3600Seconds. Range: 60–86400
line_itemsarrayNoMax 50 items. Display-only
custom_fieldsarrayNoMax 20 fields
displayobjectNoCheckout UI settings
billingobjectNoPre-fills card billing at checkout

Branding (merchant_name, merchant_logo, brand_color, locale, collect_email) comes from the payment profile linked via profile_id — not from the request body.

Payment Methods

ValueDescription
mobile_moneyMobile money (Airtel, M-Pesa, Mixx by Yas, Halotel)
qrQR code payments
cardCredit/debit card payments

Customer Object

FieldTypeNotes
namestringMax 200 chars
phonestringCustomer phone number
emailstringValid email format

Basic Example

{
  "amount": 50000,
  "currency": "TZS",
  "allowed_methods": ["mobile_money", "qr"],
  "customer": {
    "name": "John Doe",
    "phone": "+255712345678",
    "email": "john@example.com"
  },
  "redirect_url": "https://yoursite.com/success",
  "webhook_url": "https://yoursite.com/webhooks/snippe",
  "description": "Order #12345",
  "metadata": {
    "order_id": "12345"
  },
  "expires_in": 3600
}

With Profile

{
  "profile_id": "prof_550e8400-e29b-41d4-a716-446655440000",
  "amount": 50000,
  "description": "Order #12345"
}

Response

{
  "code": 201,
  "data": {
    "id": "uuid",
    "reference": "sess_abc123def456",
    "status": "pending",
    "mode": "payment",
    "amount": 50000,
    "currency": "TZS",
    "checkout_url": "https://snippe.me/checkout/W0SzdUSHQm",
    "short_code": "Ax7kM2",
    "payment_link_url": "https://snippe.me/p/Ax7kM2",
    "merchant_name": "Acme Store",
    "merchant_logo": "https://example.com/logo.png",
    "brand_color": "#4F46E5",
    "locale": "en",
    "customer_name": "John Doe",
    "customer_phone": "+255712345678",
    "customer_email": "john@example.com",
    "allowed_methods": ["mobile_money", "qr", "card"],
    "redirect_url": "https://yoursite.com/success",
    "webhook_url": "https://yoursite.com/webhooks/snippe",
    "description": "Order #12345",
    "metadata": { "order_id": "12345" },
    "allow_custom_amount": false,
    "min_amount": 0,
    "max_amount": 0,
    "line_items": [],
    "custom_fields": [],
    "display": {
      "show_line_items": true,
      "line_items_style": "compact",
      "show_description": true,
      "show_merchant_logo": true,
      "theme": "light",
      "success_message": "Payment successful!",
      "button_text": "Pay Now"
    },
    "billing": {},
    "collect_email": false,
    "expires_at": "2026-02-26T11:00:00Z",
    "created_at": "2026-02-26T10:00:00Z"
  }
}

Line Items

Display cart items on the checkout page. Max 50 items per session.

FieldTypeRequiredNotes
idstringNoYour item identifier
namestringYesItem name
descriptionstringNoItem description
image_urlstringNoProduct image URL
quantityintYesNumber of items
unit_priceuint64YesPrice per unit (smallest currency unit)
skustringNoSKU code
categorystringNoCategory label
{
  "amount": 150000,
  "line_items": [
    {
      "id": "item_001",
      "name": "Blue T-Shirt",
      "description": "100% Cotton",
      "image_url": "https://shop.com/tshirt.jpg",
      "quantity": 2,
      "unit_price": 25000,
      "sku": "TSH-BLU-M",
      "category": "Apparel"
    },
    {
      "name": "Shipping",
      "quantity": 1,
      "unit_price": 50000
    }
  ],
  "display": {
    "show_line_items": true,
    "line_items_style": "compact"
  }
}

Custom Fields

Collect additional information from customers. Max 20 fields per session.

FieldTypeRequiredNotes
keystringYesAlphanumeric + underscore
labelstringYesDisplay label
typestringYestext, email, phone, number, date, select, textarea
requiredboolYesWhether field is required
placeholderstringNoPlaceholder text
optionsstring[]NoOptions for select type
{
  "custom_fields": [
    {
      "key": "delivery_address",
      "label": "Delivery Address",
      "type": "textarea",
      "required": true
    },
    {
      "key": "delivery_option",
      "label": "Delivery Option",
      "type": "select",
      "options": ["Standard", "Express", "Pickup"],
      "required": true
    }
  ]
}

Display Settings

FieldTypeDefaultNotes
show_line_itemsboolfalseShow cart items on checkout
line_items_stylestring"compact""compact" or "cards"
show_descriptionboolfalseShow description on checkout
show_merchant_logoboolfalseShow merchant logo
themestring"light" or "dark"
success_messagestringShown after payment
button_textstringPay button label

Metadata

Store custom key-value data with your session for reconciliation and tracking.

ConstraintLimit
Max keys50
Key length40 characters
Value length500 characters
Allowed typesstring, number, boolean
{
  "amount": 50000,
  "metadata": {
    "order_id": "ORD-12345",
    "customer_id": "cust_789",
    "source": "mobile_app",
    "campaign": "summer_sale"
  }
}

Metadata is returned in session responses and webhook payloads, making it easy to match payments to your internal records.


Billing Object (Card Payments)

Pre-fill billing details to skip the billing form at checkout.

FieldTypeRequiredNotes
first_namestringYesCardholder first name
last_namestringYesCardholder last name
addressstringYesBilling street address
citystringYesBilling city
statestringYesBilling state/region
postcodestringYesBilling postal/zip code
countrystringYesISO 3166-1 alpha-2 (e.g., TZ, KE)
phonestringNoBilling phone number

If billing is pre-filled and card is in allowed_methods, customers won't need to enter billing details at checkout.


Session Status

StatusDescription
pendingSession created, awaiting payment
activePayment in progress
completedPayment successful
expiredSession expired before payment
cancelledSession cancelled

Get Session

GET /api/v1/sessions/:reference
Authorization: Bearer <token>  # or X-API-Key: <api_key>

Returns the same shape as the create session response with status code 200.


List Sessions

GET /api/v1/sessions?limit=20&offset=0&status=completed
Authorization: Bearer <token>  # or X-API-Key: <api_key>
ParameterTypeDefaultNotes
limitint20Max 100
offsetint0Pagination offset
statusstringpending, active, completed, expired, cancelled

Response

{
  "code": 200,
  "data": [
    { /* SessionResponse */ }
  ],
  "meta": {
    "total": 42,
    "limit": 20,
    "offset": 0
  }
}

Cancel Session

POST /api/v1/sessions/:reference/cancel
Authorization: Bearer <token>  # or X-API-Key: <api_key>

Response

{
  "code": 200,
  "message": "session cancelled"
}

Only sessions in pending or active status can be cancelled. Returns 404 if not found, 400 if already in a final state.


Checkout Endpoints (Public)

These endpoints power the hosted checkout page and do not require authentication.

Load Checkout Page

GET /checkout/:token

Returns session details for rendering the checkout UI.

{
  "code": 200,
  "data": {
    "reference": "sess_abc123def456",
    "status": "pending",
    "amount": 50000,
    "currency": "TZS",
    "merchant_name": "Acme Store",
    "merchant_logo": "https://example.com/logo.png",
    "brand_color": "#4F46E5",
    "locale": "en",
    "customer_name": "John Doe",
    "customer_phone": "+255712345678",
    "customer_email": "john@example.com",
    "allowed_methods": ["mobile_money", "qr", "card"],
    "description": "Order #12345",
    "collect_email": false,
    "allow_custom_amount": false,
    "min_amount": 0,
    "max_amount": 0,
    "line_items": [],
    "custom_fields": [],
    "display": {},
    "expires_at": "2026-02-26T11:00:00Z"
  }
}
StatusDescription
404Token not found
410Session expired, completed, or cancelled

Submit Payment

POST /checkout/:token/pay
Content-Type: application/json
FieldTypeRequiredNotes
payment_methodstringYesmobile_money, qr, card
amountuint64Only when allow_custom_amountMust be between min_amount and max_amount
customer_namestringNo
customer_phonestringYes (mobile_money)
customer_emailstringNo
billingobjectYes (card)Unless pre-filled at session creation

Response

{
  "code": 200,
  "data": {
    "attempt_id": "attempt_xyz789",
    "status": "pending",
    "payment_url": "https://gateway.com/pay?token=abc",
    "qr_code": "data:image/png;base64,...",
    "payment_token": "token_abc123",
    "expires_at": "2026-02-26T10:05:00Z"
  }
}

Only one of payment_url, qr_code, or payment_token is populated depending on the payment method.

Poll Payment Status

GET /checkout/:token/status
{
  "code": 200,
  "data": {
    "reference": "sess_abc123def456",
    "status": "completed",
    "redirect_url": "https://yoursite.com/success"
  }
}

Poll every 2–3 seconds. Redirect the customer when status is "completed".

Cancel Payment Attempt

POST /checkout/:token/cancel/:attemptID
{
  "code": 200,
  "message": "payment attempt cancelled"
}

Cancels a specific payment attempt. The session remains open for the customer to try again.


Example Flows

Fixed Amount (E-commerce)

{
  "amount": 50000,
  "description": "Order #12345"
}

Custom Amount (Donation)

{
  "allow_custom_amount": true,
  "min_amount": 1000,
  "max_amount": 500000,
  "description": "Donation"
}

Custom Amount with Suggested Default

{
  "allow_custom_amount": true,
  "amount": 10000,
  "min_amount": 1000,
  "max_amount": 500000
}

Rich Checkout (Line Items + Display)

{
  "amount": 150000,
  "line_items": [
    {
      "name": "Widget",
      "quantity": 2,
      "unit_price": 75000
    }
  ],
  "display": {
    "show_line_items": true,
    "line_items_style": "cards",
    "button_text": "Buy Now"
  }
}

Donation with Tiers

{
  "allow_custom_amount": true,
  "min_amount": 5000,
  "max_amount": 1000000,
  "line_items": [
    {
      "id": "tier_1",
      "name": "1 Meal",
      "unit_price": 5000,
      "quantity": 1
    },
    {
      "id": "tier_2",
      "name": "5 Meals",
      "unit_price": 25000,
      "quantity": 1
    }
  ],
  "display": {
    "show_line_items": true,
    "line_items_style": "cards",
    "button_text": "Donate Now"
  }
}

Webhooks

When payment completes, you receive a webhook at your webhook_url:

{
  "type": "payment.completed",
  "data": {
    "reference": "pi_xyz789",
    "session_reference": "sess_abc123def456",
    "status": "completed",
    "amount": {
      "value": 50000,
      "currency": "TZS"
    },
    "customer": {
      "name": "John Doe",
      "phone": "+255712345678"
    },
    "payment_method": "mobile_money",
    "completed_at": "2026-01-29T14:35:00Z"
  }
}

See Webhooks for signature verification.


Error Responses

StatusCodeDescription
400validation_errorInvalid request data
400invalid_stateSession already in final state
404not_foundSession or token not found
409conflictPayment already in progress
410payment_expiredSession has expired

Best Practices

Always Configure Webhooks

Don't rely solely on redirect URLs. Webhooks ensure you're notified even if the customer closes their browser.

Use Payment Profiles

Create profiles for consistent branding across all checkouts.

Pre-fill Customer Data

When you have customer info, pass it in the customer object to speed up checkout.

Include Metadata

Store your internal order ID in metadata for easy reconciliation.

On this page