PineTree API
PineTree API
Launch payment sessions and connect checkout flows through one clean API.
Webhooks & Events
Send real-time payment updates to your app as transactions move through checkout.
A Seamless API Integration
PineTree API gives developers one clean way to manage separate payment rails, wallet logic, provider rules, and status handling, developers can connect through PineTree and build around a simpler, unified payment engine.
Built for the payment layer behind your product
PineTree API is designed to sit behind the checkout experience and handle the payment flow from start to finish. Developers can create sessions, send customers into a guided payment experience, track each transaction as it moves through its lifecycle, and return clean payment results back into their own app or merchant system.
Create Payment Sessions
- Launch hosted or custom checkout flows
- Send amount, currency, and payment details
- Support payment links and checkout buttons
- Build one API connection for digital payments
Unified Payment Engine
- Connect to PineTree through one clean interface
- Route checkout activity through supported rails
- Reduce separate payment logic across networks
- Keep provider complexity behind the scenes
Real-Time Payment Status
- Track payments through clear lifecycle states
- Use Waiting, Processing, Confirmed, Failed, and Expired
- Update dashboards, receipts, and order flows
- Keep customer and merchant screens aligned
Developer-Friendly Integration
- Designed for websites, apps, and merchant tools
- Simple structure for payment session creation
- Works with hosted checkout and custom flows
- Keeps private infrastructure and secrets protected
Documentation
Overview
PineTree API lets developers create checkout sessions, redirect customers to hosted checkout, retrieve payment status, verify signed webhook events, and reconcile payments across enabled payment rails.
Early access: PineTree Payments is currently in early access. API access, SDK packages, hosted checkout, and card-processing availability may require approved merchant access.
At a glance
| Base URL | https://app.pinetree-payments.com |
|---|---|
| API prefix | /api/v1 |
| Server auth | Authorization: Bearer pt_live_* |
| Browser auth | X-PineTree-Public-Key: pk_live_* |
| Webhook schema | payments-v1 |
| Crypto rails | solana, base, bitcoin_lightning |
| Card rail | shift4 for approved merchants. |
| Fulfillment event | payment.confirmed |
Core flow
- Create a Checkout Session from your server using a secret API key.
- Redirect the customer to the returned
checkoutUrl. - The customer completes payment on an enabled crypto or card rail.
- PineTree sends a signed webhook event to your endpoint.
- Verify the webhook signature and deduplicate using
PineTree-Event-Id. - Fulfill the order only after
payment.confirmed.
Fulfillment rule: Do not fulfill from the customer redirect alone. Failed, Expired, and Incomplete payments must never trigger fulfillment.
Quickstart
Create a checkout session server-side, redirect the customer, then fulfill only after a confirmed webhook.
1. Install the Node SDK
npm install @pinetreepayments/node
2. Create a checkout session
import { PineTree } from "@pinetreepayments/node"
const pinetree = new PineTree(process.env.PINETREE_API_KEY)
const session = await pinetree.checkout.sessions.create(
{
amount: 2500,
currency: "USD",
reference: "order_1042",
customer: {
email: "jane@example.com"
},
successUrl: "https://yoursite.com/paid",
cancelUrl: "https://yoursite.com/cancel",
metadata: {
cartId: "cart_123"
}
},
{
idempotencyKey: "order_1042"
}
)
res.redirect(session.checkoutUrl)
3. Verify webhooks before fulfillment
app.post(
"/webhooks/pinetree",
express.raw({ type: "application/json" }),
async (req, res) => {
let event
try {
event = pinetree.webhooks.constructEvent(
req.body,
req.headers,
process.env.PINETREE_WEBHOOK_SECRET
)
} catch (error) {
return res.status(400).send("Invalid webhook signature")
}
if (await alreadyProcessed(event.eventId)) {
return res.json({ received: true, duplicate: true })
}
if (event.type === "payment.confirmed") {
await fulfillOrder(event.data.object.reference)
await markProcessed(event.eventId)
}
res.json({ received: true })
}
)
Use express.raw() or the equivalent raw-body handler in your framework. Do not JSON-parse the request body before verifying the webhook signature.
Authentication
Use secret keys on your server. Use browser keys only for public checkout creation. Never expose secret API keys in frontend code.
| Credential | Prefix | Where to use |
|---|---|---|
| Secret API key | pt_live_* | Server REST API calls and backend SDK usage. |
| Public browser key | pk_live_* | Browser checkout creation only. |
Required server headers
Authorization: Bearer pt_live_your_api_key_here
Content-Type: application/json
Optional idempotency header
Idempotency-Key: order_1042
The Idempotency-Key header is optional but strongly recommended on POST /api/v1/checkout/sessions to prevent duplicate sessions on retries.
Browser checkout header
X-PineTree-Public-Key: pk_live_your_public_key_here
Content-Type: application/json
Security requirement: Never use pt_live_* keys in frontend JavaScript, browser code, mobile apps, or public repositories. Revoke any secret key that may have been exposed.
API Keys
Create narrowly scoped keys from the Developer area. Use the smallest scope set required for each integration. Secret keys are only shown once at creation time.
Recommended environment variables
PINETREE_API_KEY=pt_live_a1b2c3d4e5f6...
PINETREE_WEBHOOK_SECRET=whsec_...
NEXT_PUBLIC_PINETREE_PUBLIC_KEY=pk_live_...
Permissions
| Permission | Allows |
|---|---|
checkout.sessions:create | Create Checkout Sessions. |
checkout.sessions:read | List and retrieve Checkout Sessions. |
checkout.sessions:write | Cancel or expire Checkout Sessions. |
payments:read | Retrieve Payment objects. |
checkout.links:create | Payment-link creation where enabled. |
webhooks:read | List Webhook Deliveries. |
webhooks:write | Retry Webhook Deliveries. |
Best practices
- Grant only the scopes required for the integration.
- Use separate keys for different services or environments.
- Store secret keys in environment variables or a secrets manager.
- Monitor last-used timestamps in the dashboard.
- Revoke compromised or unused keys.
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 manually.
POST /api/v1/checkout/sessions
Creates a hosted Checkout Session.
- Authentication
- Secret API key.
- Required scope
checkout.sessions:create- Common errors
missing_api_key,missing_permission,invalid_amount,invalid_json,invalid_rails,idempotency_key_conflict
Request
{
"amount": 2500,
"currency": "USD",
"reference": "order_1042",
"customer": {
"email": "jane@example.com"
},
"successUrl": "https://yoursite.com/paid",
"cancelUrl": "https://yoursite.com/cancel",
"metadata": {
"cartId": "cart_123"
},
"rails": ["solana", "base"]
}
Response
{
"id": "cs_01abc",
"object": "checkout.session",
"status": "open",
"amount": 2500,
"currency": "USD",
"reference": "order_1042",
"customer": {
"email": "jane@example.com"
},
"metadata": {
"cartId": "cart_123"
},
"checkoutUrl": "https://app.pinetree-payments.com/checkout/tok_01abc...",
"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"
}
GET /api/v1/checkout/sessions
Lists Checkout Sessions.
- Authentication
- Secret API key.
- Required scope
checkout.sessions:readorcheckout.sessions:create- Query params
status,reference,limit,cursor,createdAfter,createdBefore
List response
{
"object": "list",
"data": [
{
"id": "cs_01abc",
"object": "checkout.session",
"status": "open"
}
],
"hasMore": true,
"nextCursor": "eyJjcmVhdGVkQXQiOi..."
}
GET /api/v1/checkout/sessions/{id}
Retrieves a Checkout Session by ID.
- Authentication
- Secret API key.
- Required scope
checkout.sessions:readorcheckout.sessions:create
POST /api/v1/checkout/sessions/{id}/cancel
Cancels an open Checkout Session.
- Authentication
- Secret API key.
- Required scope
checkout.sessions:writeorcheckout.sessions:create
POST /api/v1/checkout/sessions/{id}/expire
Expires an open Checkout Session.
- Authentication
- Secret API key.
- Required scope
checkout.sessions:writeorcheckout.sessions:create
Browser Checkout
Browser checkout lets your frontend create a limited Checkout Session using a public browser key. Public keys are safe to embed in browser JavaScript, but they can only create checkout sessions.
Public key limitation: pk_live_* keys cannot retrieve payments, list webhook deliveries, retry webhooks, or access private merchant data.
POST /api/v1/browser/checkout/sessions
Creates a browser Checkout Session using X-PineTree-Public-Key.
- Authentication
X-PineTree-Public-Key: pk_live_*- Common errors
invalid_public_key,origin_not_allowed,invalid_request
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: 2500,
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: 2500,
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"
export function App() {
return (
<PineTreeProvider publicKey="pk_live_your_public_key_here">
<CheckoutButton />
</PineTreeProvider>
)
}
function CheckoutButton() {
return (
<PineTreeCheckoutButton
amount={2500}
currency="USD"
reference="order_1042"
successUrl={window.location.origin + "/paid"}
cancelUrl={window.location.origin + "/cancel"}
>
Pay with Crypto
</PineTreeCheckoutButton>
)
}
Payments
Use Payments for reconciliation, status lookup, and back-office workflows. Use webhook events as the primary fulfillment signal.
GET /api/v1/payments/{id}
Retrieves a Payment object.
- Authentication
- Secret API key.
- Required scope
payments:read
Status naming: The visible state Confirmed maps to API status paid. Do not check for status === "confirmed". For polling, check status === "paid". For fulfillment, use payment.confirmed.
Payment object
| Field | Type | Description |
|---|---|---|
id | string | PineTree payment ID. |
object | string | Always payment. |
status | string | Current payment status. paid means confirmed. |
amount | integer | Amount in cents. |
currency | string | Fiat currency. |
rail | string | Payment path, such as solana or base. |
asset | string | Asset paid by the customer, such as USDC, SOL, or BTC. |
reference | string | Merchant reference. |
checkoutSessionId | string | Backing Checkout Session ID when present. |
confirmedAt | string | Confirmation timestamp when present. |
metadata | object | Public metadata from session creation. |
Example response
{
"id": "pay_01abc",
"object": "payment",
"status": "paid",
"amount": 2500,
"currency": "USD",
"rail": "solana",
"asset": "USDC",
"reference": "order_1042",
"checkoutSessionId": "cs_01abc",
"confirmedAt": "2026-06-22T18:00:05.000Z",
"metadata": {
"cartId": "cart_123"
}
}
Session Statuses
Checkout Session status tracks the aggregate lifecycle of the session. Session status and Payment status are related but not always identical.
| Status | Meaning | Terminal |
|---|---|---|
open | Session created and waiting for customer payment. | No |
processing | Customer submitted payment; awaiting network or provider confirmation. | No |
paid | Payment confirmed. Session completed successfully. | Yes |
failed | Payment attempt failed. | Yes |
expired | Session expired without confirmed payment. | Yes |
canceled | Session canceled by merchant. | Yes |
For fulfillment, use payment.confirmed or checkout.session.completed webhooks. Use session retrieval for UI state and reconciliation.
Rails & Assets
Rails are payment paths. Assets are what the customer pays on that path. PineTree stores and returns both values separately.
| Rail | Assets / Currency | Description |
|---|---|---|
solana | SOL, USDC | Solana Pay payments. |
base | ETH, USDC | Base network payments. |
bitcoin_lightning | BTC | Lightning invoice payments. |
shift4 | Card / USD | Card payments for approved merchants. Contact support to enable. |
Token-specific tracking
PineTree does not combine the rail and asset into one rail name. Instead, both values are stored and returned separately.
| Payment | Rail | Asset | Display |
|---|---|---|---|
| Solana SOL | solana | SOL | Solana Pay · SOL |
| Solana USDC | solana | USDC | Solana Pay · USDC |
| Base ETH | base | ETH | Base Pay · ETH |
| Base USDC | base | USDC | Base Pay · USDC |
| Bitcoin Lightning | bitcoin_lightning | BTC | Lightning · BTC |
| Card | shift4 | USD | Card · USD |
Do not combine rail and asset: Use rail for the payment path and asset for what the customer paid.
Restrict rails on a session
{
"amount": 2500,
"currency": "USD",
"rails": ["solana", "base"]
}
Card processing: Shift4 card processing is available for approved merchants. Stripe and Fluid Pay are early-access card provider paths and are not generally public card-processing rails yet.
Payment States
Confirmed is the successful visible payment state. Failed, Expired, and Incomplete are separate terminal outcomes.
| Visible state | API status | Webhook event | Terminal | Fulfill? |
|---|---|---|---|---|
| Waiting | open / pending | payment.pending | No | No |
| Processing | processing | payment.processing | No | No |
| Confirmed | paid | payment.confirmed | Yes | Yes |
| Failed | failed | payment.failed | Yes | No |
| Expired | expired | payment.expired | Yes | No |
| Incomplete | incomplete | payment.incomplete | Yes | No |
The visible state Confirmed maps to status: "paid" in the API Payment object. Do not check status === "confirmed".
Webhooks
Webhook deliveries are signed with PineTree headers. Verify the raw body before processing.
| Header | Description |
|---|---|
PineTree-Signature | HMAC-SHA256 signature. Verify before processing. |
PineTree-Timestamp | Timestamp used in the signature and replay protection. |
PineTree-Event-Id | Unique event ID for deduplication. |
PineTree-Event-Schema | payments-v1 |
PineTree-Webhook-Version | Compatibility header. Canonical schema header is PineTree-Event-Schema. |
Signature formula
signed_payload = PineTree-Timestamp + "." + raw_request_body
expected_signature = HMAC_SHA256(webhook_secret, signed_payload)
Verification
const event = pinetree.webhooks.constructEvent(
req.body,
req.headers,
process.env.PINETREE_WEBHOOK_SECRET
)
Use raw request body verification. Return 2xx only after durable processing. PineTree delivers events at least once, so store PineTree-Event-Id and deduplicate.
Webhook Payload
Every PineTree webhook is delivered as a JSON object with a standard payments-v1 envelope.
Event envelope
{
"eventId": "evt_01abc",
"object": "event",
"type": "payment.confirmed",
"schema": "payments-v1",
"createdAt": "2026-06-22T18:00:00.000Z",
"livemode": true,
"data": {
"object": {
"id": "pay_01abc",
"object": "payment",
"status": "paid",
"network": "solana",
"amount": 2500,
"currency": "USD",
"reference": "order_1042",
"confirmedAt": "2026-06-22T18:00:05.000Z",
"metadata": {
"cartId": "cart_123"
}
}
}
}
network vs rail: Webhook payment events use network for the rail identifier. The REST Payment object uses rail for the same concept. Use event.data.object.network inside webhook handlers.
Webhook Events
Implemented merchant webhook events in the payments-v1 schema.
| Event | Object | When it fires |
|---|---|---|
payment.created | payment | Payment object created. |
payment.pending | payment | Waiting for customer or network action. |
payment.processing | payment | Payment detected, awaiting confirmation. |
payment.confirmed | payment | Payment completed. Fulfill the order. |
payment.failed | payment | Payment failed. |
payment.expired | payment | Payment timed out. |
payment.canceled | payment | Payment canceled. |
payment.incomplete | payment | Customer abandoned or no funds were sent. |
payment.refunded | payment | Payment refunded. |
checkout.session.created | checkout.session | Checkout Session created. |
checkout.session.processing | checkout.session | Checkout Session payment in progress. |
checkout.session.completed | checkout.session | Checkout Session payment confirmed. |
checkout.session.failed | checkout.session | Checkout Session payment failed. |
checkout.session.expired | checkout.session | Checkout Session expired. |
checkout.session.canceled | checkout.session | Checkout Session canceled. |
payment_link.created | payment_link | Payment link created. |
payment_link.disabled | payment_link | Payment link disabled. |
payment_link.expired | payment_link | Payment link expired. |
Use the canonical event names above for new integrations.
Webhook Deliveries
Use Webhook Deliveries to inspect delivery history and retry failed events.
GET /api/v1/webhook-deliveries
Lists Webhook Deliveries.
- Authentication
- Secret API key.
- Required scope
webhooks:read- Query params
status,eventType,limit,cursor
POST /api/v1/webhook-deliveries/{id}/retry
Retries a failed Webhook Delivery.
- Authentication
- Secret API key.
- Required scope
webhooks:write
Delivery statuses
| Status | Description |
|---|---|
pending | Queued for delivery or retry. |
delivered | Endpoint returned a 2xx response. |
failed | Last delivery attempt failed. |
dead_letter | All retry attempts exhausted. Requires attention or manual retry. |
Retry schedule
| Attempt | Delay before retry |
|---|---|
| 1 | 60 seconds |
| 2 | 120 seconds |
| 3 | 240 seconds |
| 4 | 480 seconds |
| 5 | 960 seconds |
| 6 | 1,800 seconds |
| 7–10 | 3,600 seconds |
| After 10 | dead_letter |
Node SDK retry
const delivery = await pinetree.webhookDeliveries.retry("wdel_01abc")
console.log(delivery.status)
Errors
Errors return a consistent JSON structure with a type, code, message, and request ID.
{
"error": {
"type": "authentication_error",
"code": "missing_api_key",
"message": "A PineTree API key is required.",
"requestId": "req_01abc"
}
}
| Type | Status | Meaning |
|---|---|---|
authentication_error | 401 | Missing or invalid key. |
authorization_error | 403 | Key lacks required permission. |
invalid_request_error | 400 | Malformed request. |
idempotency_error | 409 | Idempotency conflict. |
not_found_error | 404 | Object does not exist or is not accessible. |
api_error | 500 | PineTree service error. |
Common error codes
| Code | Meaning |
|---|---|
missing_api_key | No Authorization header provided. |
invalid_api_key | Key not found, revoked, or wrong format. |
missing_permission | Key does not have the required scope. |
invalid_amount | Amount is missing, zero, or invalid. |
invalid_public_key | Browser public key is invalid or revoked. |
checkout_session_not_found | No session found for the provided ID. |
payment_not_found | No payment found for the provided ID. |
webhook_delivery_not_found | No delivery found for the provided ID. |
idempotency_key_conflict | Same key used with a different request body. |
idempotency_request_in_progress | Request with this key is still in flight. |
Idempotency
Use idempotency keys to safely retry Checkout Session creation without duplicate sessions.
REST example
POST /api/v1/checkout/sessions HTTP/1.1
Authorization: Bearer pt_live_...
Content-Type: application/json
Idempotency-Key: order_1042
{
"amount": 2500,
"currency": "USD",
"reference": "order_1042"
}
Node SDK example
const session = await pinetree.checkout.sessions.create(
{
amount: 2500,
currency: "USD",
reference: "order_1042"
},
{
idempotencyKey: "order_1042"
}
)
| Scenario | Result |
|---|---|
| Same key and same body | Returns original session. |
| Same key and different body | Returns idempotency_key_conflict. |
| Request still in progress | Returns idempotency_request_in_progress. |
Use a stable, unique value per order. Do not reuse the same idempotency key for different orders.
SDKs
Use the Node SDK on servers, the JavaScript SDK in browser flows, and the React SDK for checkout components.
SDK packages are available to approved early-access merchants. Contact info@pinetree-payments.com for access.
Node SDK
npm install @pinetreepayments/node
import { PineTree } from "@pinetreepayments/node"
const pinetree = new PineTree(process.env.PINETREE_API_KEY)
const session = await pinetree.checkout.sessions.create({
amount: 2500,
currency: "USD",
reference: "order_1042"
})
const payment = await pinetree.payments.retrieve("pay_01abc")
const deliveries = await pinetree.webhookDeliveries.list({
status: "failed"
})
await pinetree.webhookDeliveries.retry("wdel_01abc")
JavaScript SDK
npm install @pinetreepayments/js
React SDK
npm install @pinetreepayments/react
Testing
PineTree currently uses live keys only. There is no separate sandbox or test-mode key prefix. Test with small live amounts and an HTTPS tunnel for local webhook development.
ngrok http 3000
# Register your HTTPS URL in Developer - Webhooks
https://abc123.ngrok-free.app/webhooks/pinetree
Testing checklist
- Create a checkout session.
- Complete a small live payment.
- Verify
payment.confirmedarrives. - Replay the same event and confirm deduplication.
- Test failed, expired, and incomplete paths.
- Temporarily return
500and verify webhook retry behavior.
Go Live
Complete these checks before accepting real payments at volume.
| Area | Checklist |
|---|---|
| API keys | Secret key created with minimal permissions and stored server-side. Public key used only for browser checkout. |
| Webhooks | HTTPS endpoint configured, raw-body verification enabled, duplicate event handling in place. |
| Payment flows | Confirmed payments fulfill orders. Failed, Expired, and Incomplete payments do not fulfill. |
| Rails | Enabled payment rails are configured and tested with small live amounts. |
| Cards | Card processing enabled only after approved provider onboarding. |
| Support | Team knows where to find requestId, Checkout Session ID, Payment ID, and webhook Event ID. |
Do not fulfill based on the customer redirect alone. Always rely on verified webhook events.
Not Yet Supported
The following capabilities are planned for future PineTree API versions. They are not required for the current hosted checkout and webhook integration flow.
| Capability | Notes |
|---|---|
| Refund API | Refunds are currently processed through the merchant dashboard. A REST endpoint is planned. |
| Payout / settlement API | Settlement preferences are configured in the dashboard. A programmatic API is planned. |
| Disputes API | Dispute management will be available after card processing is generally available. |
| Sandbox / test-mode keys | PineTree currently uses live keys only. A dedicated test environment is planned. |
| Advanced reporting API | Reports are currently available from the dashboard. A REST reporting endpoint is planned. |
| Stripe card processing | Early access. Contact support for access. |
| Fluid Pay card processing | Early access. Contact support for access. |
| Recurring billing | One-time checkout sessions are supported. Subscriptions are on the roadmap. |
| Customer objects | Customer data currently lives on sessions and payments as metadata. |
| Invoice API | Not yet available. |
Support
Contact PineTree for integration help, API access, SDK issues, and merchant onboarding questions.
| info@pinetree-payments.com | |
| Phone | 417-718-2692 |
Include this in support requests
- Merchant account email or merchant ID.
- API endpoint used.
requestIdfrom the error response.- Checkout Session ID.
- Payment ID.
- Webhook Event ID.
- Error type and code.
- Approximate timestamp in UTC.

