PineTree API

Developer Infrastructure

Cloud based infrastructure that connects supported providers, payment methods, and transaction data through one developer-friendly platform.

Webhooks & Events

Receive standardized payment events as transactions move through the PineTree lifecycle.

One API for Modern Payments

PineTree gives developers a consistent way to create payment sessions, connect supported providers, and track transactions across cards and digital payment rails.

One integration

Create payments through one consistent interface instead of rebuilding checkout logic for every supported provider.

Standardized transaction states

Work with a unified transaction model across cards, cryptocurrency, hosted checkout, and in-person payments.

PineTree Developer dashboard showing API keys, webhooks, SDKs, and integrations

Real-time payment events

Keep applications synchronized with transaction updates, payment status changes, provider responses, and webhook events through PineTree’s unified API.

PineTree API

Create payment sessions for hosted checkout, payment links, applications, marketplaces, and custom commerce experiences. Your product sends the transaction details while PineTree creates the payment record and coordinates the appropriate checkout flow.
Connect your application to PineTree instead of maintaining separate transaction logic for every supported provider and payment rail. PineTree’s provider layer normalizes external payment responses while preserving the merchant’s connected provider relationship and configuration.
Receive real-time events as transactions move through standardized lifecycle states such as Created, Pending, Processing, Confirmed, Failed, and Incomplete. Use these updates to synchronize orders, customer screens, receipts, merchant dashboards, and internal workflows.
Build payment functionality into websites, software platforms, retail systems, and commerce applications through one consistent integration layer. PineTree manages payment orchestration and provider communication while your team controls the customer experience and surrounding business logic.

Built to power the payments behind your product

PineTree API gives developers a consistent way to create payment sessions, launch hosted or custom checkout flows, track transactions through standardized lifecycle states, and return clear payment results to their application or merchant system. PineTree coordinates the payment experience across supported providers and payment rails while your product retains control of the customer journey and surrounding business logic.

PineTree API helps developers create payments, connect supported providers, and track transaction updates through one consistent integration.

Developer tools built into PineTree

Payment Session API

  • Create payment sessions from your application
  • Pass amounts, currencies, references, and return URLs
  • Launch hosted checkout, payment links, or custom flows
  • Retrieve structured transaction results through one API

Provider Abstraction

  • Connect supported providers through one integration layer
  • Use a consistent request and response structure
  • Avoid rebuilding payment logic for every provider
  • Expand payment capabilities without changing your core flow

Standardized Events

  • Receive real-time updates through PineTree webhooks
  • Build around consistent transaction lifecycle states
  • Synchronize orders, receipts, dashboards, and fulfillment
  • Handle confirmed, failed, expired, and incomplete payments

Reliable Integration Controls

  • Use merchant-scoped credentials and transaction records
  • Protect duplicate requests with idempotent operations
  • Keep provider credentials and private routing logic isolated
  • Build for websites, platforms, marketplaces, and applications
PineTree API

Developer Documentation

PineTree API lets developers create checkout sessions, retrieve payment status, receive signed webhook events, and reconcile payments across enabled payment rails.

PineTree API uses versioned REST endpoints. The current API path prefix is /api/v1.

Early Access PineTree Payments is currently in early access. API access, SDK packages, and hosted checkout are available to approved merchants. Contact info@pinetree-payments.com to get started.

At a Glance

Base URL
app.pinetree-payments.com
API prefix
/api/v1
Server auth
Bearer pt_live_*
Browser auth
X-PineTree-Public-Key
Webhook schema
payments-v1
Confirmed state
status: "paid"

Supported Rails

RailAssetsAvailability
solanaSOL, USDCLive
baseETH, USDCLive
bitcoin_lightningBTCLive
shift4Card / USDEnabled merchants
stripeCard / USDEarly access
fluidpayCard / USDEarly access

See Providers, Rails & Assets for what backs each rail and current availability.

Core Flow

The standard integration follows these steps:

  1. Create a Checkout Session from your server using a secret API key.
  2. Redirect the customer to the checkoutUrl returned in the response.
  3. The customer completes payment on an enabled rail.
  4. PineTree sends a signed payment.confirmed webhook to your endpoint.
  5. Verify the webhook signature, deduplicate on eventId, then fulfill the order.
Fulfillment rule Only fulfill orders after payment.confirmed (API status: "paid"). Never fulfill on the customer redirect alone, or while a payment is Processing, Failed, Expired, or Incomplete.
Getting Started

Quickstart

Create a checkout session server-side, redirect the customer, then fulfill only after a confirmed webhook.

Step 1 — Install the Node SDK

npm install @pinetreepayments/node

Then create an API key in your PineTree dashboard under Developer → API Keys. Copy the full pt_live_* key — it is only shown once.

Step 2 — Create a Checkout Session

Call this from your server. Never from a browser.

import { PineTree } from "@pinetreepayments/node"

const pinetree = new PineTree(process.env.PINETREE_API_KEY)

const session = await pinetree.checkout.sessions.create({
  amount: 49.99,
  currency: "USD",
  reference: "order_1042",
  successUrl: "https://yoursite.com/paid",
  cancelUrl: "https://yoursite.com/cancel"
})

res.redirect(session.checkoutUrl)

Step 3 — Configure Your Webhook Endpoint

In your PineTree dashboard, go to Developer → Webhooks. Add your HTTPS endpoint URL and copy the signing secret. The endpoint must be publicly reachable and return a 2xx response after durable processing.

Step 4 — Verify and Fulfill

app.post("/webhooks/pinetree", express.raw({ type: "application/json" }), (req, res) => {
  const event = pinetree.webhooks.constructEvent(
    req.body,
    req.headers,
    process.env.PINETREE_WEBHOOK_SECRET
  )

  if (event.type === "payment.confirmed") {
    fulfillOrder(event.data.object.reference)
  }

  res.json({ received: true })
})
You're ready At this point your integration can create sessions, redirect customers, receive signed webhook events, and fulfill orders. Continue reading for full API reference, all event types, and go-live requirements.
Security

Authentication

Use secret keys on your server. Use browser keys only for public checkout creation. Never expose secret API keys in frontend code.

Key Types

CredentialPrefixWhere to Use
Secret API keypt_live_*Server REST API calls and backend SDK usage.
Public browser keypk_live_*Browser checkout creation only.
Security requirement Never use pt_live_* keys in frontend JavaScript, browser code, mobile apps, or public repositories. Treat secret keys like passwords. Revoke any key that may have been exposed.

Required Headers (Server)

Authorization: Bearer pt_live_your_api_key_here
Content-Type: application/json

Optional Header

Idempotency-Key: order_1042

The Idempotency-Key header is optional but strongly recommended on POST /api/v1/checkout/sessions. See Idempotency for details.

Browser Checkout Header

When creating checkout sessions from browser code, use a public key instead of a secret key:

X-PineTree-Public-Key: pk_live_your_public_key_here
Content-Type: application/json

Public keys (pk_live_*) are created and revoked from Developer → API Keys → Public Keys in your PineTree dashboard, the same way secret keys are managed.

Invalid Credentials

ScenarioHTTP statusError code
No Authorization header401missing_api_key
Key not found, revoked, or wrong format401invalid_api_key
No X-PineTree-Public-Key header on a browser request401missing_public_key
Public key not found, revoked, or wrong format401invalid_public_key
Key lacks required permission403missing_permission

PineTree issues pt_live_* keys only. There is no pt_test_* variant. All keys operate against live payment infrastructure.

Credentials

API Keys

Create permission-limited keys from the Developer area. Use the smallest permission set required for each integration. Secret keys are only shown once at creation time.

Secret Keys

Secret keys (pt_live_*) authenticate server-to-server REST requests. Store them in environment variables, not in source code or configuration files that may be committed to version control.

# .env
PINETREE_API_KEY=pt_live_a1b2c3d4e5f6...
PINETREE_WEBHOOK_SECRET=your_webhook_signing_secret

Public Browser Keys

Public keys (pk_live_*) are safe to embed in browser JavaScript. They can only create checkout sessions and cannot access payments, webhook deliveries, or any private merchant data.

Permissions

PermissionGrants
checkout.sessions:createCreate Checkout Sessions via POST /api/v1/checkout/sessions. Also accepted in place of checkout.sessions:read/write for back-compat.
checkout.sessions:readList and retrieve Checkout Sessions.
checkout.sessions:writeCancel or expire Checkout Sessions.
payments:readRetrieve Payment objects.
checkout.links:createReserved for payment-link creation. Granted by default today but not yet enforced by any live endpoint.
webhooks:readList Webhook Deliveries.
webhooks:writeRetry Webhook Deliveries.

Best Practices

  • Grant only the permissions required for the specific integration.
  • Use separate keys for different services or environments.
  • Revoke any key that may have been accidentally exposed.
  • Monitor Last Used timestamps in the dashboard to identify stale keys.
  • Store secret keys in a secrets manager or environment variables — never in code.
Hosted Checkout

Checkout Sessions

Checkout Sessions create hosted payment pages. Create sessions from your server and fulfill orders only after confirmed webhook events.

checkoutUrl is opaque Redirect customers directly to checkoutUrl. Do not construct or parse this URL — treat it as an opaque redirect destination.

Create a Session

POST/api/v1/checkout/sessions

Creates a hosted Checkout Session. Requires permission checkout.sessions:create

Request body

{
  "amount": 49.99,                     // required — major currency units. 49.99 means $49.99
  "currency": "USD",                   // optional — defaults to "USD"
  "reference": "order_1042",           // optional — your order/cart ID
  "customer": {
    "email": "jane@example.com"        // optional
  },
  "successUrl": "https://yoursite.com/paid",
  "cancelUrl":  "https://yoursite.com/cancel",
  "metadata": { "cartId": "cart_123" },
  "rails": ["solana", "base"]          // optional — limit available payment methods
}

Response

{
  "id": "3f9a1c2e-4b6d-4a11-9c2f-8e1a6d2b7f40",
  "object": "checkout.session",
  "status": "open",
  "amount": 49.99,
  "currency": "USD",
  "reference": "order_1042",
  "customer": { "email": "jane@example.com" },
  "metadata": { "cartId": "cart_123" },
  "checkoutUrl": "https://app.pinetree-payments.com/checkout/8f2a1b9c3d4e5f60",
  "paymentId": null,
  "supportedRails": ["solana", "base", "bitcoin_lightning"],
  "successUrl": "https://yoursite.com/paid",
  "cancelUrl": "https://yoursite.com/cancel",
  "createdAt": "2026-06-22T12:00:00.000Z",
  "expiresAt": "2026-06-23T12:00:00.000Z"
}
Object IDs are plain UUIDs id on Checkout Sessions and Payments is a plain UUID with no prefix — do not write validation or routing logic that assumes a cs_ or pay_ prefix. Webhook event IDs use an evt_ prefix — see Webhook Payload.
Error codeMeaning
missing_api_keyNo Authorization header.
missing_permissionKey lacks checkout.sessions:create.
invalid_amountamount missing, zero, or not a positive number.
invalid_jsonRequest body is not valid JSON.
invalid_railsUnrecognized rail value in rails array.
invalid_customerMalformed customer object.
invalid_metadataMalformed metadata object.
invalid_urlMalformed successUrl or cancelUrl.
idempotency_key_conflictSame Idempotency-Key, different body.
idempotency_request_in_progressRequest with this key already in flight.

List Sessions

GET/api/v1/checkout/sessions

Returns a paginated list of Checkout Sessions. Requires permission checkout.sessions:read (or checkout.sessions:create)

Query parameterTypeDescription
limitinteger1–100. Defaults to 10.
statusstringOne of open, processing, paid, failed, expired, canceled.
cursorstringPagination cursor from a previous response's nextCursor. Also accepted as starting_after.
referencestringFilter by merchant reference.
created_afterISO 8601Include sessions created after this date.
created_beforeISO 8601Include sessions created before this date.
// List response envelope
{
  "object": "list",
  "data": [ /* checkout.session objects */ ],
  "hasMore": true,
  "nextCursor": "eyJjcmVhdGVkQXQiOi..."
}
Error codeMeaning
invalid_filterlimit out of range.
unsupported_statusUnrecognized status filter value.
invalid_cursorMalformed pagination cursor.

Retrieve a Session

GET/api/v1/checkout/sessions/{id}

Retrieves a single Checkout Session. Requires permission checkout.sessions:read or checkout.sessions:create

Cancel a Session

POST/api/v1/checkout/sessions/{id}/cancel

Cancels an open Checkout Session. Requires permission checkout.sessions:write (or checkout.sessions:create). Returns checkout_session_not_cancelable if the session is not in a cancelable state.

Expire a Session

POST/api/v1/checkout/sessions/{id}/expire

Immediately expires an open Checkout Session. Requires permission checkout.sessions:write (or checkout.sessions:create). Returns checkout_session_not_expirable if the session is not in an expirable state.

Session Statuses

StatusDescription
openWaiting for customer payment
processingPayment broadcast; awaiting confirmation
paidConfirmed — fulfill the order
failedPayment failed
expiredSession expired without payment
canceledExplicitly canceled by merchant

Sessions that are not explicitly canceled or expired will expire automatically after 24 hours.

Frontend Integration

Browser Checkout

Create checkout sessions from browser code using a public key. Public keys are safe to embed in frontend JavaScript and cannot access private merchant data.

Public keys are limited by design A pk_live_* key can only create checkout sessions. It cannot retrieve payments, list webhook deliveries, or perform any back-office operation.

Browser Checkout Endpoint

POST/api/v1/browser/checkout/sessions

Creates a Checkout Session using a public browser key. Authentication: X-PineTree-Public-Key header.

Error codeMeaning
missing_public_keyNo X-PineTree-Public-Key header.
invalid_public_keyPublic key not found, revoked, or wrong format.
invalid_amountamount missing, zero, or not a positive number.
invalid_jsonRequest body is not valid JSON.
invalid_railsUnrecognized rail value in rails array.

Raw Fetch Example

const response = await fetch(
  "https://app.pinetree-payments.com/api/v1/browser/checkout/sessions",
  {
    method: "POST",
    headers: {
      "X-PineTree-Public-Key": "pk_live_your_public_key_here",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      amount: 49.99,
      currency: "USD",
      reference: "order_1042",
      successUrl: window.location.origin + "/paid",
      cancelUrl:  window.location.origin + "/cancel",
    }),
  }
)
const session = await response.json()
window.location.href = session.checkoutUrl

JavaScript SDK

npm install @pinetreepayments/js
import { PineTreeJS } from "@pinetreepayments/js"

const ptjs = new PineTreeJS("pk_live_your_public_key_here")

const session = await ptjs.checkout.createSession({
  amount: 49.99,
  currency: "USD",
  reference: "order_1042",
  successUrl: window.location.origin + "/paid",
  cancelUrl:  window.location.origin + "/cancel",
})
ptjs.checkout.open(session)

React SDK

npm install @pinetreepayments/react
import { PineTreeProvider, PineTreeCheckoutButton } from "@pinetreepayments/react"

function App() {
  return (
    <PineTreeProvider publicKey="pk_live_your_public_key_here">
      <CheckoutPage />
    </PineTreeProvider>
  )
}

function CheckoutPage() {
  return (
    <PineTreeCheckoutButton
      amount={49.99}
      currency="USD"
      reference="order_1042"
      successUrl={window.location.origin + "/paid"}
      cancelUrl={window.location.origin + "/cancel"}
    >
      Pay with Crypto
    </PineTreeCheckoutButton>
  )
}
SDK packages are available to approved early-access merchants. Contact info@pinetree-payments.com for access.
Payment Objects

Payments

Use Payments for reconciliation, status lookup, and back-office workflows. Use payment.confirmed webhooks — not polling — for order fulfillment.

Retrieve a Payment

GET/api/v1/payments/{id}

Retrieves a Payment object by ID. Requires permission payments:read. There is no list-payments endpoint today — retrieve payments by ID only.

Payment Object Fields

FieldTypeDescription
idstringPineTree payment ID. A plain UUID — no prefix.
objectstringAlways "payment".
statusstringCurrent payment status. See status mapping below.
amountnumberAmount in major currency units (USD).
currencystringFiat currency, e.g. "USD".
railstringPayment rail, e.g. "solana", "base", "bitcoin_lightning".
networkstringSame value as rail, returned alongside it for compatibility with webhook payloads.
referencestringYour merchant reference / order ID.
createdAtstringISO 8601 creation timestamp.
updatedAtstringISO 8601 last-update timestamp.
metadataobjectPublic metadata from session creation.
No asset, checkoutSessionId, or confirmedAt on the REST object The Payment object does not currently return a separate asset field (e.g. distinguishing SOL vs. USDC on the same rail), a checkoutSessionId back-reference, or a confirmedAt timestamp. confirmedAt is available on the webhook payload only.
Status naming — important The Payment object returns "status": "paid" when the payment is confirmed. The visible customer-facing state is called Confirmed. Do not check for status === "confirmed"confirmed is never a real API status value, only paid is. Only fulfill orders after payment.confirmed (status: "paid"); never fulfill on the customer redirect alone, or while Processing, Failed, Expired, or Incomplete.

Payment Object

{
  "id": "3f9a1c2e-4b6d-4a11-9c2f-8e1a6d2b7f40",
  "object": "payment",
  "status": "paid",
  "amount": 49.99,
  "currency": "USD",
  "rail": "solana",
  "network": "solana",
  "reference": "order_1042",
  "metadata": {},
  "createdAt": "2026-06-22T17:59:30.000Z",
  "updatedAt": "2026-06-22T18:00:05.000Z"
}

Status Mapping

Visible stateAPI status valueWebhook eventTerminal
Waitingopen / pendingpayment.pendingNo
Processingprocessingpayment.processingNo
Confirmedpaidpayment.confirmedYes
Failedfailedpayment.failedYes
Expiredexpiredpayment.expiredYes
Incompleteincompletepayment.incompleteYes
Checkout Sessions

Session Statuses

Checkout Session status tracks the aggregate state of the session, which may span multiple payment attempts. Session status and Payment status use similar but not identical labels.

StatusMeaningTerminal
openSession created and the checkout page is waiting for the customer to begin payment.No
processingCustomer has submitted payment; PineTree is awaiting on-chain or provider confirmation.No
paidPayment confirmed. Fulfill the order on the corresponding payment.confirmed event.Yes
failedPayment attempt failed. Customer may need to retry if a new session is created.Yes
expiredSession expired without a confirmed payment. Sessions expire automatically after 24 hours.Yes
canceledSession was explicitly canceled by the merchant via the API or dashboard.Yes
Webhook vs polling The preferred way to detect paid is the checkout.session.completed or payment.confirmed webhook event — not polling the session status. Rely on webhooks for fulfillment; use session retrieval for UI state only.
Payment Methods

Providers, Rails & Assets

Rails are payment paths. Assets are what the customer pays on that path.

Supported Rails

RailAssets / CurrencyDescription
solanaSOL, USDCSolana Pay payments.
baseETH, USDCBase network payments.
bitcoin_lightningBTCLightning invoice payments.
shift4Card / USDCard payments for approved merchants. Contact support to enable.

Card processing via Shift4 is available to approved merchants through the PineTree merchant onboarding flow. Contact info@pinetree-payments.com to apply. Additional card processors are available in early access.

Provider vs. Rail vs. Asset

A provider is the company powering payments — the external payment platform or processor that moves the money (Shift4, Stripe, FluidPay, Speed). A rail is the payment network — the path a Checkout Session travels on (solana, base, bitcoin_lightning, shift4, stripe, fluidpay). An asset is the currency/token the customer actually pays with (SOL, USDC, ETH, BTC, USD).

solana and base are native PineTree rails: there is no separate third-party processor behind them, so the rail identifier is the provider, and both are live today. bitcoin_lightning, shift4, stripe, and fluidpay each have a distinct backing provider — see the table below for current availability.

Providers

ProviderRailAssets / CurrencyAvailabilityNotes
Solana PaysolanaSOL, USDCLiveNative Solana rail. Not a third-party processor.
Base PaybaseETH, USDCLiveNative Base rail. Not a third-party processor. Coinbase Wallet is one supported wallet on this rail — it is not the provider.
Speedbitcoin_lightningBTCLiveSpeed powers Lightning settlement. Requires either a merchant-connected Speed account or PineTree's platform-level Lightning settlement to be enabled for your environment. Lightning via Nostr Wallet Connect (NWC) is also supported.
Shift4shift4Card / USDEnabled merchantsCard processing available to approved merchants through PineTree's onboarding flow.
StripestripeCard / USDEarly accessAvailable in early access through PineTree's Stripe Connect integration (Connect, Payments, and Terminal). Availability depends on merchant onboarding and which capabilities are enabled on the connected account. Contact support to begin onboarding.
FluidPayfluidpayCard / USDEarly accessAvailable in early access. Merchant onboarding and provider approval are required before processing is enabled. Contact support for current status.

Rail and Asset by Payment Type

PaymentrailDisplay label
Solana SOLsolanaSolana Pay · SOL
Solana USDCsolanaSolana Pay · USDC
Base ETHbaseBase Pay · ETH
Base USDCbaseBase Pay · USDC
Bitcoin Lightningbitcoin_lightningLightning · BTC
Card (Shift4)shift4Card · USD
There is no asset field rail (called network in webhook payloads) identifies the payment path, such as solana or base. There is currently no separate asset field to distinguish which token was paid on a multi-asset rail — for example, SOL vs. USDC on Solana, or ETH vs. USDC on Base. Do not build logic that expects one; use the rails table above as the source of truth for which assets a rail can settle.

Restricting Rails on a Session

Pass the optional rails array when creating a session to limit which payment methods are offered on the hosted checkout page:

{
  "amount": 49.99,
  "currency": "USD",
  "rails": ["solana", "base"]    // only show Solana and Base options
}
Lifecycle

Payment States

Confirmed is the positive visible payment state and the only state that should trigger order fulfillment. All other terminal states — Failed, Expired, Incomplete — must never trigger fulfillment.

Visible stateAPI statusWebhook eventTerminalFulfill?
Waitingopenpayment.pendingNoNo
Processingprocessingpayment.processingNoNo
Confirmedpaidpayment.confirmedYesYes ✓
Failedfailedpayment.failedYesNo
Expiredexpiredpayment.expiredYesNo
Incompleteincompletepayment.incompleteYesNo
Only fulfill orders after payment.confirmed (API status: "paid"). Never fulfill on the customer redirect alone, or while a payment is Processing, Failed, Expired, or Incomplete. The visible state Confirmed maps to status: "paid" in the API Payment object — do not check status === "confirmed", it will not match.

State Lifecycle

  CREATED → PENDING → PROCESSING → CONFIRMED  (status: "paid")
                                  └→ FAILED     (status: "failed")
             └→ INCOMPLETE                      (status: "incomplete")
  CREATED → EXPIRED                             (status: "expired")
Events

Webhooks

Webhook deliveries are signed with PineTree headers. Verify the raw body before processing.

Setup

  1. Go to Developer → Webhooks in your PineTree dashboard.
  2. Enter your HTTPS endpoint URL. HTTP endpoints are not accepted.
  3. Copy the signing secret shown on the page. It is only shown once.
  4. Store the secret as PINETREE_WEBHOOK_SECRET in your environment.

Webhook Headers

HeaderDescription
PineTree-SignatureHMAC-SHA256 hex signature. Verify this before processing.
PineTree-TimestampISO 8601 timestamp used in the signature. PineTree rejects events older than 5 minutes.
PineTree-Event-IdUnique event ID. Store and use for deduplication.
PineTree-Event-SchemaAlways payments-v1.
PineTree-Webhook-VersionLegacy compatibility header carrying the same schema value. Canonical header is PineTree-Event-Schema.

Signature Formula

PineTree signs each delivery with HMAC-SHA256 over the timestamp and the raw request body:

signed_payload    = PineTree-Timestamp + "." + raw_request_body
expected_sig      = HMAC_SHA256(webhook_secret, signed_payload)
header_sig        = PineTree-Signature header value

// Verified if: constant_time_equals(expected_sig, header_sig)
// and: abs(now - PineTree-Timestamp) < 300 seconds

Verification — Node SDK

// CRITICAL: Use express.raw() — do NOT parse the body before this call
app.post(
  "/webhooks/pinetree",
  express.raw({ type: "application/json" }),
  (req, res) => {
    let event
    try {
      event = pinetree.webhooks.constructEvent(
        req.body,
        req.headers,
        process.env.PINETREE_WEBHOOK_SECRET
      )
    } catch (err) {
      return res.status(400).send("Signature verification failed")
    }

    const alreadySeen = await checkAndStoreEventId(event.eventId)
    if (alreadySeen) return res.json({ received: true })

    if (event.type === "payment.confirmed") {
      await fulfillOrder(event.data.object.reference)
    }

    res.json({ received: true })
  }
)

Reliability Rules

  • PineTree delivers events at least once. Your endpoint must handle duplicate events idempotently.
  • Return a 2xx response only after writing event data to durable storage.
  • If your endpoint returns a non-2xx, PineTree will retry with backoff — see Webhook Deliveries.
  • Do not fulfill orders based on the customer redirect URL alone. Always wait for payment.confirmed.
Event Structure

Webhook Payload

Every PineTree webhook is delivered as a JSON object with a standard payments-v1 envelope. The event type, schema, and event ID are at the top level. The payment or session object is inside data.object.

Event Envelope

{
  "eventId":   "evt_01abc...",          // unique — store for deduplication
  "object":    "event",
  "type":      "payment.confirmed",
  "schema":    "payments-v1",
  "createdAt": "2026-06-22T18:00:00.000Z",
  "livemode":  true,
  "data": {
    "object": { /* payment or checkout.session object */ }
  }
}

Payment Event Payload

{
  "eventId":   "evt_01abc...",
  "object":    "event",
  "type":      "payment.confirmed",
  "schema":    "payments-v1",
  "createdAt": "2026-06-22T18:00:00.000Z",
  "livemode":  true,
  "data": {
    "object": {
      "id":          "3f9a1c2e-4b6d-4a11-9c2f-8e1a6d2b7f40",
      "object":      "payment",
      "status":      "paid",
      "network":     "solana",
      "amount":      49.99,
      "currency":    "USD",
      "reference":   "order_1042",
      "confirmedAt": "2026-06-22T18:00:05.000Z",
      "metadata":    { "cartId": "cart_123" }
    }
  }
}
network vs rail Webhook payment events use the field name network to identify the payment rail (e.g., "solana", "base"). The REST API Payment object uses rail for the same value (and also returns network alongside it). Use event.data.object.network in webhook handlers.

Checkout Session Event Payload

{
  "eventId":   "evt_02def...",
  "object":    "event",
  "type":      "checkout.session.completed",
  "schema":    "payments-v1",
  "createdAt": "2026-06-22T18:00:05.000Z",
  "livemode":  true,
  "data": {
    "object": {
      "id":             "3f9a1c2e-4b6d-4a11-9c2f-8e1a6d2b7f40",
      "object":         "checkout.session",
      "status":         "paid",
      "amount":         49.99,
      "currency":       "USD",
      "reference":      "order_1042",
      "customer":       { "email": "jane@example.com" },
      "metadata":       { "cartId": "cart_123" },
      "checkoutUrl":    "https://app.pinetree-payments.com/checkout/8f2a1b9c3d4e5f60",
      "paymentId":      "5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e",
      "supportedRails": ["solana", "base", "bitcoin_lightning"],
      "successUrl":     "https://yoursite.com/paid",
      "cancelUrl":      "https://yoursite.com/cancel",
      "createdAt":      "2026-06-22T17:59:00.000Z",
      "expiresAt":      "2026-06-23T17:59:00.000Z"
    }
  }
}

Payment Event Object Fields

FieldTypeDescription
idstringPayment ID. A plain UUID — no prefix.
objectstringAlways "payment".
statusstringPayment status at time of event.
networkstringRail identifier (e.g., solana, base).
amountnumberAmount in major currency units.
currencystringFiat currency.
referencestringYour merchant reference / order ID.
confirmedAtstringISO 8601 confirmation time. Present on payment.confirmed.
metadataobjectPublic metadata from session creation.
Event Catalog

Webhook Events

Implemented merchant webhook events in the payments-v1 schema. Use eventId to deduplicate — PineTree delivers at least once.

Payment Events

EventWhen it firesFulfill?
payment.createdPayment object first created.No
payment.pendingWaiting for customer or network action.No
payment.processingPayment detected, awaiting confirmation.No
payment.confirmedPayment fully confirmed. Primary fulfillment trigger.Yes ✓
payment.failedPayment failed.No
payment.expiredPayment timed out.No
payment.canceledPayment was canceled.No
payment.incompleteCustomer abandoned or no funds were sent.No
payment.refundedPayment was refunded.No

Checkout Session Events

EventWhen it fires
checkout.session.createdSession created via POST /api/v1/checkout/sessions.
checkout.session.processingSession has a payment in processing state.
checkout.session.completedSession payment confirmed. Object carries the full checkout session.
checkout.session.failedSession payment failed.
checkout.session.expiredSession expired after 24 hours without confirmed payment.
checkout.session.canceledSession canceled by merchant.

Payment Link Events

EventWhen it fires
payment_link.createdPayment link created.
payment_link.disabledPayment link disabled or deactivated.
payment_link.expiredPayment link expired.

Legacy Events

Legacy eventNormalizes to
checkout.session.paidcheckout.session.completed
payment.cancelledpayment.canceled

Legacy event aliases from prior API versions are accepted and normalized to their canonical equivalents. Use the canonical event names listed above for all new integrations.

Reliability

Webhook Deliveries

Use Webhook Deliveries to inspect delivery history, debug failed events, and manually trigger retries from your server tooling or automation.

GET/api/v1/webhook-deliveries

Lists Webhook Deliveries. Requires permission webhooks:read

Query parameterDescription
limit1–100. Defaults to 20.
statusOne of pending, delivered, failed, dead_letter.
eventTypeFilter by event type, e.g. payment.confirmed.
cursorPagination cursor from a previous response's nextCursor.
// List response envelope
{
  "object": "list",
  "data": [ /* webhook.delivery objects */ ],
  "hasMore": false,
  "nextCursor": null
}
POST/api/v1/webhook-deliveries/{id}/retry

Manually retries a failed or dead-letter delivery. Requires permission webhooks:write

// Node SDK — manual retry
const delivery = await pinetree.webhookDeliveries.retry("6c7d8e9f-0a1b-4c2d-9e3f-4a5b6c7d8e9f")
console.log(delivery.status)         // "delivered" if retry succeeded
console.log(delivery.attemptCount)   // total attempts including this retry

Webhook Delivery Object

{
  "id": "6c7d8e9f-0a1b-4c2d-9e3f-4a5b6c7d8e9f",
  "object": "webhook.delivery",
  "eventType": "payment.confirmed",
  "status": "delivered",
  "attemptCount": 1,
  "nextAttemptAt": null,
  "lastAttemptAt": "2026-06-22T18:00:06.000Z",
  "lastStatusCode": 200,
  "lastError": null,
  "deliveredAt": "2026-06-22T18:00:06.000Z",
  "deadLetteredAt": null,
  "createdAt": "2026-06-22T18:00:00.000Z"
}

Delivery Statuses

StatusMeaning
pendingQueued for initial delivery or scheduled retry.
deliveredYour endpoint returned a 2xx response.
failedLast delivery attempt failed; retry is scheduled.
dead_letterAll retry attempts exhausted. Requires manual retry or operator intervention.

Automatic Retry Schedule

PineTree retries failed deliveries with backoff. After 10 failed attempts the delivery moves to dead_letter.

AttemptDelay before retry
160 seconds
2120 seconds
3240 seconds
4480 seconds
5960 seconds
61,800 seconds (30 min)
7–103,600 seconds (1 hour)
After 10dead_letter — no further automatic retries

Use POST /api/v1/webhook-deliveries/{id}/retry to manually retry a dead_letter delivery after fixing the issue on your endpoint.

Error Handling

Errors

All errors return a consistent JSON structure with a type, code, message, and requestId. Include the requestId when contacting support.

Error Response Shape

{
  "error": {
    "type":      "authentication_error",
    "code":      "missing_api_key",
    "message":   "A PineTree API key is required.",
    "requestId": "3f9a1c2e-4b6d-4a11-9c2f-8e1a6d2b7f40"
  }
}

Error Types

TypeHTTP statusMeaning
authentication_error401Missing or invalid API key.
authorization_error403Key lacks a required permission.
invalid_request_error400Malformed request or invalid field value.
idempotency_error409Idempotency key conflict or request in progress.
not_found_error404Resource does not exist or is not accessible.
api_error500PineTree service error. Safe to retry with backoff.

Common Error Codes

CodeTypeMeaning
missing_api_keyauthentication_errorNo Authorization header provided.
invalid_api_keyauthentication_errorKey not found, revoked, or wrong format.
missing_public_keyauthentication_errorNo X-PineTree-Public-Key header on a browser request.
invalid_public_keyauthentication_errorThe public key value is invalid or revoked.
missing_permissionauthorization_errorKey does not have the required permission for this endpoint.
invalid_amountinvalid_request_erroramount is missing, zero, or not a positive number.
invalid_jsoninvalid_request_errorRequest body is not valid JSON.
invalid_railsinvalid_request_errorUnrecognized rail value in the rails array.
invalid_customerinvalid_request_errorMalformed customer object.
invalid_metadatainvalid_request_errorMalformed metadata object.
invalid_urlinvalid_request_errorMalformed successUrl or cancelUrl.
invalid_filterinvalid_request_errorlimit out of range on a list request.
unsupported_statusinvalid_request_errorUnrecognized status filter value.
invalid_cursorinvalid_request_errorMalformed pagination cursor.
missing_session_idinvalid_request_errorNo session ID in the request path.
checkout_session_not_cancelableinvalid_request_errorSession is not in a cancelable state.
checkout_session_not_expirableinvalid_request_errorSession is not in an expirable state.
checkout_session_not_foundnot_found_errorNo session found for the provided ID.
payment_not_foundnot_found_errorNo payment found for the provided ID.
webhook_delivery_not_foundnot_found_errorNo webhook delivery found for the provided ID.
idempotency_key_conflictidempotency_errorSame key was used with a different request body.
idempotency_request_in_progressidempotency_errorA request with this key is still in flight.
idempotency_storage_failureapi_errorPineTree failed to persist idempotency state. Safe to retry.
internal_errorapi_errorUnexpected server error. Safe to retry with backoff.

Node SDK

The Node SDK throws V1ApiError on any non-2xx response, carrying the same type, code, and requestId shown above. Check error.type/error.code to branch your handling.

Retries

Idempotency

Add an Idempotency-Key header to POST /api/v1/checkout/sessions to safely retry the request without creating duplicate sessions. The key is optional but strongly recommended for any production integration.

How It Works

ScenarioResult
Same key + same request bodyReturns the original session. No duplicate is created.
Same key + different request body409 — idempotency_key_conflict. Do not retry.
Request still in flight409 — idempotency_request_in_progress. Wait and retry.

REST Example

POST /api/v1/checkout/sessions HTTP/1.1
Authorization: Bearer pt_live_...
Content-Type: application/json
Idempotency-Key: order_1042          ← use your stable order ID

{
  "amount": 49.99,
  "currency": "USD",
  "reference": "order_1042"
}

Node SDK Example

const session = await pinetree.checkout.sessions.create(
  {
    amount: 49.99,
    currency: "USD",
    reference: "order_1042",
    successUrl: "https://yoursite.com/paid",
    cancelUrl:  "https://yoursite.com/cancel",
  },
  { idempotencyKey: "order_1042" }
)

Best Practices

  • Use a stable, unique value per order — your order ID or cart ID is ideal.
  • Do not reuse the same idempotency key for different orders.
  • On network failure or timeout, retry with the same idempotency key and same body.
  • If you receive a 409 idempotency_key_conflict, do not retry — generate a new session for the new request.
Libraries

SDKs

PineTree provides official SDKs for Node.js (server), JavaScript (browser), and React. Use the Node SDK for all server-side operations. Use the JS and React SDKs with public browser keys only.

SDK packages are available to approved early-access merchants. Contact info@pinetree-payments.com to request access. Once approved, install from npm using the commands below.

Node SDK — @pinetreepayments/node

npm install @pinetreepayments/node

Use on your server with a pt_live_* secret key. Requires Node.js 18+.

import { PineTree } from "@pinetreepayments/node"

const pinetree = new PineTree(process.env.PINETREE_API_KEY)

// Checkout Sessions
const session = await pinetree.checkout.sessions.create(
  { amount: 49.99, currency: "USD", reference: "order_1042" },
  { idempotencyKey: "order_1042" }
)
await pinetree.checkout.sessions.retrieve(session.id)
await pinetree.checkout.sessions.list({ status: "paid", limit: 20 })
await pinetree.checkout.sessions.cancel(session.id)
await pinetree.checkout.sessions.expire(session.id)

// Payments — retrieve only; there is no payments.list()
const payment = await pinetree.payments.retrieve("3f9a1c2e-4b6d-4a11-9c2f-8e1a6d2b7f40")
// → payment.status === "paid" means confirmed

// Webhook Deliveries
const deliveries = await pinetree.webhookDeliveries.list({ status: "failed" })
await pinetree.webhookDeliveries.retry("6c7d8e9f-0a1b-4c2d-9e3f-4a5b6c7d8e9f")

// Verify a webhook signature
const event = pinetree.webhooks.constructEvent(
  req.body,           // raw Buffer — must be raw, not parsed
  req.headers,
  process.env.PINETREE_WEBHOOK_SECRET
)

JavaScript SDK — @pinetreepayments/js

npm install @pinetreepayments/js

Use in browser environments with a pk_live_* public key. Cannot use secret keys.

import { PineTreeJS } from "@pinetreepayments/js"

const ptjs = new PineTreeJS("pk_live_your_public_key_here")

const session = await ptjs.checkout.createSession({
  amount: 49.99,
  currency: "USD",
  reference: "order_1042",
  successUrl: window.location.origin + "/paid",
  cancelUrl:  window.location.origin + "/cancel",
})

ptjs.checkout.open(session)   // redirects to hosted checkout

React SDK — @pinetreepayments/react

npm install @pinetreepayments/react

React components and hooks for checkout buttons. Uses @pinetreepayments/js under the hood with a pk_live_* public key.

import { PineTreeProvider, PineTreeCheckoutButton } from "@pinetreepayments/react"

export function Providers({ children }) {
  return (
    <PineTreeProvider publicKey="pk_live_your_public_key_here">
      {children}
    </PineTreeProvider>
  )
}

export function BuyButton({ orderId, amount }) {
  return (
    <PineTreeCheckoutButton
      amount={amount}
      currency="USD"
      reference={orderId}
      successUrl={`${window.location.origin}/paid`}
      cancelUrl={`${window.location.origin}/cancel`}
    >
      Pay with Crypto
    </PineTreeCheckoutButton>
  )
}
Validation

Testing

PineTree currently issues only pt_live_* keys — there is no separate PineTree sandbox or test-mode environment. Test PineTree integrations with small live amounts and an HTTPS tunnel for local webhook development.

Live keys only — provider sandboxes are separate PineTree issues pt_live_* keys only — there is no pt_test_* variant, and no PineTree-side sandbox. Individual payment providers behind a rail (for example Stripe) may still offer their own sandbox or test mode for testing directly with that provider; that is independent of PineTree's API, which always operates on live keys. amount uses major currency units.

Test Webhooks Locally

Use an HTTPS tunnel to expose your local server to PineTree's webhook delivery:

ngrok http 3000

# Register the HTTPS URL in Developer → Webhooks:
https://abc123.ngrok-free.app/webhooks/pinetree

Integration Test Checklist

  • Happy path — Create session → customer pays → payment.confirmed received → order fulfilled.
  • Failed paymentpayment.failed received → order not fulfilled.
  • Expired session — Session times out → payment.expired received → order not fulfilled.
  • Incomplete session — Customer abandons → payment.incomplete received → order not fulfilled.
  • Duplicate delivery — Replay the same event ID → your handler skips fulfillment on the second call.
  • Signature tampered — Modify the payload → constructEvent throws → handler returns 400.
  • Idempotent creation — Create session twice with same Idempotency-Key → same session returned, no duplicate.
  • Webhook retry — Make your endpoint return 500 → verify retry appears in Webhook Deliveries list.

Useful Environment Variables

PINETREE_API_KEY=pt_live_a1b2c3d4...
PINETREE_WEBHOOK_SECRET=your_webhook_signing_secret
NEXT_PUBLIC_PINETREE_PUBLIC_KEY=pk_live_...  # browser usage
Launch

Go-Live Checklist

Complete all items before accepting real customer payments at volume.

API Keys & Credentials

  • Secret API key created with the minimum required permissions.
  • Secret key stored in environment variables, not in source code.
  • Secret key is not present in any frontend bundle, browser console, or public repository.
  • Public browser key (pk_live_*) used for all frontend checkout flows.

Webhooks

  • Webhook endpoint is HTTPS only.
  • Webhook signing secret stored as PINETREE_WEBHOOK_SECRET.
  • PineTree-Signature verified on every incoming event using raw body.
  • Body is not JSON-parsed before calling constructEvent.
  • Endpoint returns 2xx after durable write, not before.
  • PineTree-Event-Id stored and checked to deduplicate at-least-once delivery.
  • Dead-letter deliveries are monitored and alerted on.

Payment Flows

  • Orders are fulfilled only after payment.confirmed.
  • Customer redirect to successUrl does not trigger fulfillment alone.
  • payment.failed, payment.expired, payment.incomplete never trigger fulfillment.
  • Happy path tested end-to-end with a small live transaction.
  • Failed and expired flows tested.
  • Duplicate event handling verified.

Rails & Providers

  • At least one payment rail is configured and verified in the merchant dashboard.
  • Treasury wallet addresses are verified for each enabled crypto rail.
  • If Shift4 card processing is enabled: merchant application is approved for production processing.
  • If Stripe card processing is enabled (early access): Connect account is active with the capabilities your integration needs — Connect, Payments, Terminal — enabled.
  • If FluidPay card processing is enabled (early access): merchant onboarding and provider approval are complete.
  • Session successUrl and cancelUrl are production HTTPS URLs.
Roadmap

Not Yet Supported

The following capabilities are planned for future PineTree API versions. None of these are required for the current hosted checkout and webhook integration pattern.

CapabilityNotes
Refund APIRefunds are currently processed through the merchant dashboard. A REST endpoint is planned.
Payout / settlement APISettlement preferences and withdrawals are configured in the dashboard. Programmatic settlement API is planned.
Disputes APIDispute management for card payments will be available once card processing is generally available.
Sandbox / test-mode keysPineTree currently uses live keys only. A dedicated test environment is a planned roadmap item.
Advanced reporting APIReports are available for download and email from the merchant dashboard. A REST reporting endpoint is planned.
Stripe card processingIn early access. Contact support for access.
Fluid Pay card processingIn early access. Contact support for access.
Recurring billing / subscriptionsOne-time checkout sessions are supported. Subscription and recurring payment APIs are on the roadmap.
Customer objectsCustomer data is currently stored as metadata on sessions and payments. A dedicated Customer API is planned.
Invoice APIMerchant-generated invoices are not yet available via the API.
If a capability on this list is critical for your integration, contact info@pinetree-payments.com. Priority is informed by early-access merchant requirements.
Help

Support

Contact PineTree for integration help, API access, and merchant onboarding questions.

Contact

Email
info@pinetree-payments.com
Phone
417-718-2692

What to Include in a Support Request

Include the following in support requests: merchant account email, API endpoint, requestId from the error response, Checkout Session ID, Payment ID, webhook Event ID, error type and code, and approximate timestamp (UTC).

Common Questions

How do I get API access?
Email info@pinetree-payments.com with your business name, website, and expected monthly transaction volume.
How do I enable card processing?
Shift4 is available to approved merchants through PineTree's onboarding flow. Stripe and FluidPay card processing are in early access — contact support for current status. Crypto rails are available immediately after account approval.
Are there sandbox or test keys?
Not from PineTree — PineTree currently issues only pt_live_* keys, with no PineTree-side sandbox. A dedicated PineTree test environment is on the roadmap. Individual providers behind a rail, such as Stripe, may offer their own separate sandbox for testing with that provider directly. Test PineTree integrations by running small-amount live transactions with real wallets.
Why isn't my webhook arriving?
Check Developer → Webhooks → Deliveries in the dashboard. If deliveries show as failed, your endpoint may be returning a non-2xx status or timing out. If status is dead_letter, use POST /api/v1/webhook-deliveries/{id}/retry after fixing the endpoint issue.