REST API Quickstart
This guide walks through your first end-to-end Pinch integration using the REST API. By the end you'll have sent an Order, sent a Return against it, and read the decision Pinch returned for each.
If you're on Shopify and don't need custom logic in your own stack, see the Shopify Quickstart instead — the turnkey app writes the same decisions to your customer tags without any code.
What you'll need
- A Pinch sandbox API key (email support@pinch.ai to request one if you don't have it)
- An HTTP client —
curl, Postman, or any language with an HTTP library - ~10 minutes
1. Set up your environment
Export your sandbox key. Throughout this guide we use qa-api.pinch.ai as the base URL — the sandbox environment. Production is api.pinch.ai.
export PINCH_API_KEY="pk_test_..."
export PINCH_BASE_URL="https://qa-api.pinch.ai"
Quick sanity check that the key works — no payload, just authentication:
curl -i $PINCH_BASE_URL/v1/order \
-H "Authorization: $PINCH_API_KEY"
You should get a 400 or 405 (because you didn't send a body) but not a 401. A 401 means the key isn't being accepted — double-check the value and the base URL prefix matches the key prefix. See Authentication.
2. Send your first Order
Pinch needs to know about an order before it can evaluate a return against it. In production this call goes out at checkout completion. In this quickstart we'll fabricate one.
- curl
- Node
- Python
curl $PINCH_BASE_URL/v1/order \
-H "Authorization: $PINCH_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"intent": "Order",
"state": "PAID",
"providerOrderId": "quickstart-order-001",
"totalPrice": { "currencyCode": "840", "amount": "239.00" },
"lineItems": [
{
"identifiers": [{ "identifierType": "SKU", "identifier": "DRESS-MAXI-M-BLUE" }],
"title": "Maxi Dress Floral",
"category": "Dress",
"quantity": "1",
"unitPrice": { "currencyCode": "840", "amount": "239.00" }
}
],
"customer": {
"firstName": "Quickstart",
"lastName": "User",
"contactInformation": [
{ "contactType": "EMail", "address": "quickstart@example.com" },
{
"contactType": "ContactAddress",
"address": {
"line1": "1234 Test St",
"city": "San Francisco",
"state": "CA",
"postalCode": "94101",
"country": "US"
},
"addressType": "Shipping"
}
]
},
"paymentInfo": [
{
"amount": { "currencyCode": "840", "amount": "239.00" },
"instrument": "CC-A",
"last4": "4242",
"BIN": "424242",
"postalCode": "94101",
"method": "NotPresent",
"paymentStatus": "pre-auth success"
}
],
"timestamp": "'"$(date +%s)000"'"
}'
const res = await fetch(`${process.env.PINCH_BASE_URL}/v1/order`, {
method: "POST",
headers: {
Authorization: process.env.PINCH_API_KEY,
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({
intent: "Order",
state: "PAID",
providerOrderId: "quickstart-order-001",
totalPrice: { currencyCode: "840", amount: "239.00" },
lineItems: [
{
identifiers: [{ identifierType: "SKU", identifier: "DRESS-MAXI-M-BLUE" }],
title: "Maxi Dress Floral",
category: "Dress",
quantity: "1",
unitPrice: { currencyCode: "840", amount: "239.00" },
},
],
customer: {
firstName: "Quickstart",
lastName: "User",
contactInformation: [
{ contactType: "EMail", address: "quickstart@example.com" },
{
contactType: "ContactAddress",
address: {
line1: "1234 Test St",
city: "San Francisco",
state: "CA",
postalCode: "94101",
country: "US",
},
addressType: "Shipping",
},
],
},
paymentInfo: [
{
amount: { currencyCode: "840", amount: "239.00" },
instrument: "CC-A",
last4: "4242",
BIN: "424242",
postalCode: "94101",
method: "NotPresent",
paymentStatus: "pre-auth success",
},
],
timestamp: String(Date.now()),
}),
});
const decision = await res.json();
console.log(decision);
import os, requests, time
res = requests.post(
f"{os.environ['PINCH_BASE_URL']}/v1/order",
headers={
"Authorization": os.environ["PINCH_API_KEY"],
"Content-Type": "application/json",
"Accept": "application/json",
},
json={
"intent": "Order",
"state": "PAID",
"providerOrderId": "quickstart-order-001",
"totalPrice": {"currencyCode": "840", "amount": "239.00"},
"lineItems": [
{
"identifiers": [
{"identifierType": "SKU", "identifier": "DRESS-MAXI-M-BLUE"}
],
"title": "Maxi Dress Floral",
"category": "Dress",
"quantity": "1",
"unitPrice": {"currencyCode": "840", "amount": "239.00"},
}
],
"customer": {
"firstName": "Quickstart",
"lastName": "User",
"contactInformation": [
{"contactType": "EMail", "address": "quickstart@example.com"},
{
"contactType": "ContactAddress",
"address": {
"line1": "1234 Test St",
"city": "San Francisco",
"state": "CA",
"postalCode": "94101",
"country": "US",
},
"addressType": "Shipping",
},
],
},
"paymentInfo": [
{
"amount": {"currencyCode": "840", "amount": "239.00"},
"instrument": "CC-A",
"last4": "4242",
"BIN": "424242",
"postalCode": "94101",
"method": "NotPresent",
"paymentStatus": "pre-auth success",
}
],
"timestamp": str(int(time.time() * 1000)),
},
)
print(res.json())
Expected response:
{
"providerOrderID": "quickstart-order-001",
"pinchOrderID": "pin_8c2f9a1b",
"cartLevelDecisions": [
{
"decisions": [
{
"decisionType": "APPROVE/DECLINE",
"decision": "APPROVE",
"subDecision": "Default Approve",
"decisionReason": "No risk signals detected",
"abuseScore": 12
}
]
}
]
}
Note the abuseScore of 12 — that's a clean, low-risk order. New customer, no history, no fingerprint, but no obvious red flags either. The decision is APPROVE.
For full field-by-field documentation see the Order API reference.
3. Send a Return against that order
A few seconds later, the same customer initiates a return:
- curl
- Node
- Python
curl $PINCH_BASE_URL/v1/return \
-H "Authorization: $PINCH_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"providerOrderId": "quickstart-order-001",
"providerReturnId": "quickstart-return-001",
"status": "REQUESTED",
"reason": "Return - Wrong size",
"returnMethod": { "returnMethod": "Drop-Off" },
"refundMode": { "refundMode": "ORIGINAL_PAYMENT" },
"returnedLineItems": [
{
"lineItem": {
"identifiers": [
{ "identifierType": "SKU", "identifier": "DRESS-MAXI-M-BLUE" }
]
},
"quantity": "1",
"reason": "Wrong size"
}
],
"timestamp": "'"$(date +%s)000"'"
}'
const res = await fetch(`${process.env.PINCH_BASE_URL}/v1/return`, {
method: "POST",
headers: {
Authorization: process.env.PINCH_API_KEY,
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({
providerOrderId: "quickstart-order-001",
providerReturnId: "quickstart-return-001",
status: "REQUESTED",
reason: "Return - Wrong size",
returnMethod: { returnMethod: "Drop-Off" },
refundMode: { refundMode: "ORIGINAL_PAYMENT" },
returnedLineItems: [
{
lineItem: {
identifiers: [{ identifierType: "SKU", identifier: "DRESS-MAXI-M-BLUE" }],
},
quantity: "1",
reason: "Wrong size",
},
],
timestamp: String(Date.now()),
}),
});
console.log(await res.json());
res = requests.post(
f"{os.environ['PINCH_BASE_URL']}/v1/return",
headers={
"Authorization": os.environ["PINCH_API_KEY"],
"Content-Type": "application/json",
"Accept": "application/json",
},
json={
"providerOrderId": "quickstart-order-001",
"providerReturnId": "quickstart-return-001",
"status": "REQUESTED",
"reason": "Return - Wrong size",
"returnMethod": {"returnMethod": "Drop-Off"},
"refundMode": {"refundMode": "ORIGINAL_PAYMENT"},
"returnedLineItems": [
{
"lineItem": {
"identifiers": [
{"identifierType": "SKU", "identifier": "DRESS-MAXI-M-BLUE"}
]
},
"quantity": "1",
"reason": "Wrong size",
}
],
"timestamp": str(int(time.time() * 1000)),
},
)
print(res.json())
Expected response:
{
"providerReturnId": "quickstart-return-001",
"pinchReturnId": "pret_3f8a1c",
"returnedLineItems": [
{
"lineItem": {
"identifierType": "SKU",
"identifier": "DRESS-MAXI-M-BLUE"
},
"decisionType": "APPROVE/DECLINE/REVIEW",
"decision": "APPROVE",
"subDecision": "Default Approve",
"reason": "First-time customer, normal return",
"abuseScore": 14
}
]
}
4. Read the decision
The decision tells your service how to handle the return:
decision | Action |
|---|---|
APPROVE | Process the refund per your normal flow. |
DECLINE | Block the return. Your service should refuse to issue a return label or refund. |
REVIEW | Route to manual review. The return appears in the Pinch dashboard's Review queue with AI Analysis. Approve or deny from there. |
In a typical custom integration you'd:
- 1Forward the customer's return request to your /returns endpoint
Your endpoint calls Pinch's
/v1/return. - 2Read the decision and act
APPROVE→ continue to refund.DECLINE→ show an error to the customer or route to manual review.REVIEW→ put the return on hold and poll the dashboard or wait for a webhook (coming soon). - 3Tell Pinch what happened
Once you've processed (or denied) the return, optionally send the disposition data later via /v1/return/disposition. This feeds the model and improves future scoring.
5. Trigger a different decision
To see Pinch actually deny something, replay the customer with high-risk signals. Send 10 more orders against the same email with no fingerprint and short time gaps, then return all of them:
for i in $(seq 2 12); do
curl -s $PINCH_BASE_URL/v1/order -H "Authorization: $PINCH_API_KEY" \
-H "Content-Type: application/json" \
-d "{ ... providerOrderId: \"quickstart-order-$i\", same customer ... }" \
> /dev/null
done
# Then return each
By the third or fourth return on the same email, the abuseScore climbs into the 60s, the decision becomes REVIEW, and the subDecision becomes Suspect Abuse. The thresholds are configured in your Returns workflow — see Workflows and Decisions to tune them.
6. Go from sandbox to production
When you're ready to ship:
- 1Request a production key
Email support with your sandbox testing summary. Production keys are issued after a quick review.
- 2Swap two values
PINCH_API_KEY→ yourpk_live_...key.PINCH_BASE_URL→https://api.pinch.ai. - 3Deploy in shadow mode for one week
Don't act on decisions yet — just log them and compare against your existing return-approval rate. This catches false-positive risk before customers see denials.
- 4Flip enforcement on
Once the decisions match your expectations on real traffic, start acting on
DECLINEandREVIEW.
What's next
Author the rules that produce Allow / Deny / Review on your traffic.
Send post-receipt item conditions back to Pinch to improve future scoring.
Every endpoint, every field, every error.
The full mental model — workflows, decisions, variables, segments.