No such blueprint
This instance publishes no blueprint at that address. The catalog lists every one it does publish.
Back to the blueprints
Blueprints
/
Full integrations
Authorize now, capture later
Hold the funds when the customer orders, capture them for what you actually shipped, and read back the record that settles.
Signed in, you can run this blueprint against your own sandbox merchant one call at a
time, with the values from each call threaded into the next.
Sign in to run it .
On this page
4 steps, 3 API calls Payments Transactions
Samples use {{API_KEY}} for your API key and {{BASE_URL}} for this instance's API address. Anything else in double braces is a value an earlier step gave you.
Hold the funds 1. Authorize the card without taking the money API call
POST
/api/transactions
Send the same request you would send for a sale, with a transaction type of Authorization. The issuer holds the funds and nothing moves. The capture below takes the money, and an authorization nobody ever captures expires on the issuer's own schedule rather than yours. Authorize at the full order amount, because a capture can go down from here and can't go up.
Reference for this operation
Values this step gives you
{{transactionId}}
The id of the authorization, from the response body's id property. {{merchantId}}
The merchant the authorization belongs to, from the response body's merchantId property. cURL CopycURL
Copied
Copy failed
curl -X POST "{{BASE_URL}}/api/transactions" \
-H "api-key: {{API_KEY}}" \
-H "Content-Type: application/json" \
-d '{
"transactionType": "Authorization",
"cardData": {
"cardNumber": "4111111111111111",
"nameOnCard": "Jane Doe",
"expirationMonth": 12,
"expirationYear": 2030,
"cvv": 123
},
"invoiceData": {
"amounts": { "base": 10.00, "total": 10.00 }
}
}'.NET Copy.NET
Copied
Copy failed
using var http = new HttpClient { BaseAddress = new Uri("{{BASE_URL}}") };
http.DefaultRequestHeaders.Add("api-key", "{{API_KEY}}");
var response = await http.PostAsJsonAsync("/api/transactions", new
{
transactionType = "Authorization",
cardData = new
{
cardNumber = "4111111111111111",
nameOnCard = "Jane Doe",
expirationMonth = 12,
expirationYear = 2030,
cvv = 123
},
invoiceData = new
{
amounts = new { @base = 10.00m, total = 10.00m }
}
});
response.EnsureSuccessStatusCode();
var authorization = await response.Content.ReadFromJsonAsync<JsonElement>();
var transactionId = authorization.GetProperty("id").GetString();
var merchantId = authorization.GetProperty("merchantId").GetString();What this step answers with
Abridged to the properties this step depends on. A real response carries more.
HTTP 200 CopyHTTP 200
Copied
Copy failed
{
"id": "9f1c2d3e-4b5a-4c7d-8e9f-0a1b2c3d4e5f",
"merchantId": "3a7b1c9d-2e4f-4a6b-8c8d-9e0f1a2b3c4d",
"transactionType": "Authorization",
"currentStage": "Authorized",
"resultCode": "Ok",
"authorizedAmount": 10.00,
"responseData": {
"resultCode": "Ok",
"resultMessage": "Approved"
}
}Capture what you shipped 2. Capture the authorization when you ship API call
POST
/api/transactions/by-merchant/{{merchantId}}/{{transactionId}}/operations
Capture for 9.00 against an authorization of 10.00. Capturing less than you held is ordinary rather than an edge case: a line went out of stock, or the shipping came in under the estimate. The network's rules release the unused part of the hold. Leave the amount out entirely to capture the whole authorization. Capture once and only once, because an authorization keeps no remainder to capture afterward, so a second shipment needs a second authorization. In production, send an idempotencyKey as well, so a retried request after a timeout can't capture twice.
Reference for this operation
Values this step gives you
cURL CopycURL
Copied
Copy failed
curl -X POST "{{BASE_URL}}/api/transactions/by-merchant/{{merchantId}}/{{transactionId}}/operations" \
-H "api-key: {{API_KEY}}" \
-H "Content-Type: application/json" \
-d '{
"operationType": "Capture",
"amount": 9.00
}'.NET Copy.NET
Copied
Copy failed
var capture = await http.PostAsJsonAsync(
$"/api/transactions/by-merchant/{merchantId}/{transactionId}/operations",
new
{
operationType = "Capture",
amount = 9.00m
});
capture.EnsureSuccessStatusCode();
var result = await capture.Content.ReadFromJsonAsync<JsonElement>();
var succeeded = result.GetProperty("success").GetBoolean();What this step answers with
Abridged to the properties this step depends on. A real response carries more.
HTTP 200 CopyHTTP 200
Copied
Copy failed
{
"success": true,
"operationType": "Capture",
"transactionId": "{{transactionId}}",
"timedOut": false
}Confirm what was captured 3. Read the transaction back API call
GET
/api/transactions/{{transactionId}}
Read the same transaction you authorized. A capture changes the record in place rather than creating a second one, so currentStage now reads Captured while authorizedAmount still holds what you originally authorized. Reconcile your order against this record rather than against the capture response. The capture response tells you whether the operation succeeded. The transaction is what settles.
Reference for this operation
Values this step gives you
cURL CopycURL
Copied
Copy failed
curl "{{BASE_URL}}/api/transactions/{{transactionId}}" \
-H "api-key: {{API_KEY}}".NET Copy.NET
Copied
Copy failed
var transaction = await http.GetFromJsonAsync<JsonElement>(
$"/api/transactions/{transactionId}");
var stage = transaction.GetProperty("currentStage").GetString();What this step answers with
Abridged to the properties this step depends on. A real response carries more.
HTTP 200 CopyHTTP 200
Copied
Copy failed
{
"id": "{{transactionId}}",
"merchantId": "{{merchantId}}",
"transactionType": "Authorization",
"currentStage": "Captured",
"resultCode": "Ok",
"authorizedAmount": 10.00,
"responseData": {
"resultCode": "Ok",
"resultMessage": "Approved"
}
}If the amount can still grow 4. Raise the authorization rather than authorizing again On your side
Raise the existing hold rather than adding a second card transaction alongside the first. A capture can only go down from the amount you authorized, and an order can still grow before you fulfill it: an added item, a tip, an extended stay. That's the IncrementalAuthorization operation type, on the same endpoint the capture above used. It sends a fresh authorization message to the network, so the issuer can refuse it, and not every processor supports it. The reference below documents it in full. This blueprint stops at the ordinary shipped-for-less flow.
Reference for this operation
Values this step gives you
Run the whole flow as one script
Every step above in one script you can copy and run. Replace {{API_KEY}} with your own API key and {{BASE_URL}} with this instance's API address, and set any value the script asks you for at the top. A step that happens outside the API stays a comment.
Code sample language >_
cURL PS
PowerShell TS
TypeScript C#
C# Py
Python
brew install jq
Copythe install command for cURL
Copied
Copy failed
cURL CopycURL
Copied
Copy failed
#!/usr/bin/env bash
# Authorize now, capture later
#
# Hold the funds when the customer orders, capture them for what you actually shipped, and read back
# the record that settles.
#
# Every API call in this blueprint, in order. Each value a call returns is passed to the calls after
# it. A step that happens outside the API is a comment, and any failed call stops the script.
set -euo pipefail
BASE_URL="{{BASE_URL}}"
API_KEY="{{API_KEY}}"
# Sends one request and prints the response body. A failed call prints the API's answer and stops
# the script.
call() {
local method="$1" path="$2" body="${3:-}" out
local args=(-sS --fail-with-body -X "$method" "$BASE_URL$path" -H "api-key: $API_KEY")
if [ -n "$body" ]; then
args+=(-H "Content-Type: application/json" -d "$body")
fi
if ! out=$(curl "${args[@]}"); then
printf '%s\n' "$out" >&2
return 1
fi
printf '%s' "$out"
}
# Percent-encodes a value for use in a URL.
urlencode() {
jq -rn --arg value "$1" '$value | @uri'
}
# Phase 1: Hold the funds
# Step 1: Authorize the card without taking the money
step1=$(call POST "/api/transactions" '{
"transactionType": "Authorization",
"cardData": {
"cardNumber": "4111111111111111",
"nameOnCard": "Jane Doe",
"expirationMonth": 12,
"expirationYear": 2030,
"cvv": 123
},
"invoiceData": {
"amounts": { "base": 10.00, "total": 10.00 }
}
}')
transactionId=$(jq -er '.id | if . == null then error("The response carried no value for transactionId.") else tostring end' <<< "$step1")
merchantId=$(jq -er '.merchantId | if . == null then error("The response carried no value for merchantId.") else tostring end' <<< "$step1")
# Phase 2: Capture what you shipped
# Step 2: Capture the authorization when you ship
call POST "/api/transactions/by-merchant/$(urlencode "$merchantId")/$(urlencode "$transactionId")/operations" '{
"operationType": "Capture",
"amount": 9.00
}' > /dev/null
# Phase 3: Confirm what was captured
# Step 3: Read the transaction back
call GET "/api/transactions/$(urlencode "$transactionId")" > /dev/null
# Phase 4: If the amount can still grow
# Step 4: Raise the authorization rather than authorizing again
# Raise the existing hold rather than adding a second card transaction alongside the first. A
# capture can only go down from the amount you authorized, and an order can still grow before you
# fulfill it: an added item, a tip, an extended stay. That's the IncrementalAuthorization operation
# type, on the same endpoint the capture above used. It sends a fresh authorization message to the
# network, so the issuer can refuse it, and not every processor supports it. The reference below
# documents it in full. This blueprint stops at the ordinary shipped-for-less flow.
Copythe install command for PowerShell
Copied
Copy failed
PowerShell CopyPowerShell
Copied
Copy failed
# Authorize now, capture later
#
# Hold the funds when the customer orders, capture them for what you actually shipped, and read back
# the record that settles.
#
# Every API call in this blueprint, in order. Each value a call returns is passed to the calls after
# it. A step that happens outside the API is a comment, and any failed call stops the script.
$ErrorActionPreference = 'Stop'
$baseUrl = '{{BASE_URL}}'
$apiKey = '{{API_KEY}}'
# Sends one request and returns the parsed response body. A failed call stops the script.
function Invoke-BlueprintCall([string] $Method, [string] $Path, [string] $Body) {
$arguments = @{
Method = $Method
Uri = $baseUrl + $Path
Headers = @{ 'api-key' = $apiKey }
}
if ($Body) {
$arguments.ContentType = 'application/json'
$arguments.Body = [System.Text.Encoding]::UTF8.GetBytes($Body)
}
Invoke-RestMethod @arguments
}
# Turns a value read off a response back into the text the API sent. A missing value stops the
# script.
function ConvertTo-CaptureValue($Value, [string] $Name) {
if ($null -eq $Value) { throw "The response carried no value for $Name." }
if ($Value -is [datetime] -and $Value.Kind -eq 'Unspecified') { return $Value.ToString('yyyy-MM-ddTHH:mm:ss.FFFFFFF', [cultureinfo]::InvariantCulture) }
if ($Value -is [datetime]) { return $Value.ToUniversalTime().ToString('o') }
if ($Value -is [bool]) { return $Value.ToString().ToLowerInvariant() }
[string]$Value
}
# Phase 1: Hold the funds
# Step 1: Authorize the card without taking the money
$body = @'
{
"transactionType": "Authorization",
"cardData": {
"cardNumber": "4111111111111111",
"nameOnCard": "Jane Doe",
"expirationMonth": 12,
"expirationYear": 2030,
"cvv": 123
},
"invoiceData": {
"amounts": { "base": 10.00, "total": 10.00 }
}
}
'@
$step1 = Invoke-BlueprintCall -Method 'POST' -Path '/api/transactions' -Body $body
$transactionId = ConvertTo-CaptureValue $step1.id 'transactionId'
$merchantId = ConvertTo-CaptureValue $step1.merchantId 'merchantId'
# Phase 2: Capture what you shipped
# Step 2: Capture the authorization when you ship
$body = @'
{
"operationType": "Capture",
"amount": 9.00
}
'@
$null = Invoke-BlueprintCall -Method 'POST' -Path "/api/transactions/by-merchant/$([uri]::EscapeDataString($merchantId))/$([uri]::EscapeDataString($transactionId))/operations" -Body $body
# Phase 3: Confirm what was captured
# Step 3: Read the transaction back
$null = Invoke-BlueprintCall -Method 'GET' -Path "/api/transactions/$([uri]::EscapeDataString($transactionId))"
# Phase 4: If the amount can still grow
# Step 4: Raise the authorization rather than authorizing again
# Raise the existing hold rather than adding a second card transaction alongside the first. A
# capture can only go down from the amount you authorized, and an order can still grow before you
# fulfill it: an added item, a tip, an extended stay. That's the IncrementalAuthorization operation
# type, on the same endpoint the capture above used. It sends a fresh authorization message to the
# network, so the issuer can refuse it, and not every processor supports it. The reference below
# documents it in full. This blueprint stops at the ordinary shipped-for-less flow.
Copythe install command for TypeScript
Copied
Copy failed
TypeScript CopyTypeScript
Copied
Copy failed
// Authorize now, capture later
//
// Hold the funds when the customer orders, capture them for what you actually shipped, and read
// back the record that settles.
//
// Every API call in this blueprint, in order. Each value a call returns is passed to the calls
// after it. A step that happens outside the API is a comment, and any failed call stops the script.
export {};
const baseUrl = '{{BASE_URL}}';
const apiKey = '{{API_KEY}}';
// Sends one request and returns the parsed response body. A failed call throws.
async function call(method: string, path: string, body?: unknown): Promise<any> {
const headers: Record<string, string> = { 'api-key': apiKey };
if (body !== undefined) {
headers['Content-Type'] = 'application/json';
}
const response = await fetch(baseUrl + path, {
method,
headers,
body: body === undefined ? undefined : JSON.stringify(body),
});
const text = await response.text();
if (!response.ok) {
throw new Error(`${method} ${path} answered ${response.status}: ${text}`);
}
return text ? JSON.parse(text) : null;
}
// Turns a value read off a response into the text the API sent. A missing value throws.
function capture(value: unknown, name: string): string {
if (value === undefined || value === null) {
throw new Error(`The response carried no value for ${name}.`);
}
return typeof value === 'string' ? value : JSON.stringify(value);
}
// Phase 1: Hold the funds
// Step 1: Authorize the card without taking the money
const step1 = await call('POST', '/api/transactions', {
"transactionType": "Authorization",
"cardData": {
"cardNumber": "4111111111111111",
"nameOnCard": "Jane Doe",
"expirationMonth": 12,
"expirationYear": 2030,
"cvv": 123
},
"invoiceData": {
"amounts": { "base": 10.00, "total": 10.00 }
}
});
const transactionId = capture(step1.id, 'transactionId');
const merchantId = capture(step1.merchantId, 'merchantId');
// Phase 2: Capture what you shipped
// Step 2: Capture the authorization when you ship
await call('POST', `/api/transactions/by-merchant/${encodeURIComponent(merchantId)}/${encodeURIComponent(transactionId)}/operations`, {
"operationType": "Capture",
"amount": 9.00
});
// Phase 3: Confirm what was captured
// Step 3: Read the transaction back
await call('GET', `/api/transactions/${encodeURIComponent(transactionId)}`);
// Phase 4: If the amount can still grow
// Step 4: Raise the authorization rather than authorizing again
// Raise the existing hold rather than adding a second card transaction alongside the first. A
// capture can only go down from the amount you authorized, and an order can still grow before you
// fulfill it: an added item, a tip, an extended stay. That's the IncrementalAuthorization operation
// type, on the same endpoint the capture above used. It sends a fresh authorization message to the
// network, so the issuer can refuse it, and not every processor supports it. The reference below
// documents it in full. This blueprint stops at the ordinary shipped-for-less flow.
Copythe install command for C#
Copied
Copy failed
C# CopyC#
Copied
Copy failed
// Authorize now, capture later
//
// Hold the funds when the customer orders, capture them for what you actually shipped, and read
// back the record that settles.
//
// Every API call in this blueprint, in order. Each value a call returns is passed to the calls
// after it. A step that happens outside the API is a comment, and any failed call stops the script.
using System.Text;
using System.Text.Json;
var baseUrl = "{{BASE_URL}}";
var apiKey = "{{API_KEY}}";
using var http = new HttpClient();
http.DefaultRequestHeaders.Add("api-key", apiKey);
// Sends one request and returns the parsed response body. A failed call throws.
async Task<JsonElement> CallAsync(string method, string path, string? body = null)
{
using var request = new HttpRequestMessage(new HttpMethod(method), baseUrl + path);
if (body is not null)
{
request.Content = new StringContent(body, Encoding.UTF8, "application/json");
}
using var response = await http.SendAsync(request);
var json = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
throw new HttpRequestException($"{method} {path} answered {(int)response.StatusCode}: {json}");
}
return json.Length == 0 ? default : JsonSerializer.Deserialize<JsonElement>(json);
}
// Turns a value read off a response into the text the API sent. A missing value throws.
static string Capture(JsonElement value, string name) => value.ValueKind switch
{
JsonValueKind.String => value.GetString()!,
JsonValueKind.Number or JsonValueKind.True or JsonValueKind.False => value.GetRawText(),
_ => throw new InvalidOperationException($"The response carried no value for {name}.")
};
// Phase 1: Hold the funds
// Step 1: Authorize the card without taking the money
var step1 = await CallAsync("POST", "/api/transactions", """
{
"transactionType": "Authorization",
"cardData": {
"cardNumber": "4111111111111111",
"nameOnCard": "Jane Doe",
"expirationMonth": 12,
"expirationYear": 2030,
"cvv": 123
},
"invoiceData": {
"amounts": { "base": 10.00, "total": 10.00 }
}
}
""");
var transactionId = Capture(step1.GetProperty("id"), "transactionId");
var merchantId = Capture(step1.GetProperty("merchantId"), "merchantId");
// Phase 2: Capture what you shipped
// Step 2: Capture the authorization when you ship
await CallAsync("POST", $"/api/transactions/by-merchant/{Uri.EscapeDataString(merchantId)}/{Uri.EscapeDataString(transactionId)}/operations", """
{
"operationType": "Capture",
"amount": 9.00
}
""");
// Phase 3: Confirm what was captured
// Step 3: Read the transaction back
await CallAsync("GET", $"/api/transactions/{Uri.EscapeDataString(transactionId)}");
// Phase 4: If the amount can still grow
// Step 4: Raise the authorization rather than authorizing again
// Raise the existing hold rather than adding a second card transaction alongside the first. A
// capture can only go down from the amount you authorized, and an order can still grow before you
// fulfill it: an added item, a tip, an extended stay. That's the IncrementalAuthorization operation
// type, on the same endpoint the capture above used. It sends a fresh authorization message to the
// network, so the issuer can refuse it, and not every processor supports it. The reference below
// documents it in full. This blueprint stops at the ordinary shipped-for-less flow.
pip install requests
Copythe install command for Python
Copied
Copy failed
Python CopyPython
Copied
Copy failed
# Authorize now, capture later
#
# Hold the funds when the customer orders, capture them for what you actually shipped, and read back
# the record that settles.
#
# Every API call in this blueprint, in order. Each value a call returns is passed to the calls after
# it. A step that happens outside the API is a comment, and any failed call stops the script.
from urllib.parse import quote
import json
import requests
BASE_URL = "{{BASE_URL}}"
API_KEY = "{{API_KEY}}"
# Sends one request and returns the parsed response body. A failed call raises.
def call(method, path, body=None):
response = requests.request(
method,
BASE_URL + path,
headers={"api-key": API_KEY},
json=body,
)
response.raise_for_status()
return response.json() if response.content else None
# Turns a value read off a response into the text the API sent. A missing value raises.
def capture(value, name):
if value is None:
raise ValueError(f"The response carried no value for {name}.")
return value if isinstance(value, str) else json.dumps(value)
# Phase 1: Hold the funds
# Step 1: Authorize the card without taking the money
step1 = call("POST", "/api/transactions", {
"transactionType": "Authorization",
"cardData": {
"cardNumber": "4111111111111111",
"nameOnCard": "Jane Doe",
"expirationMonth": 12,
"expirationYear": 2030,
"cvv": 123
},
"invoiceData": {
"amounts": { "base": 10.00, "total": 10.00 }
}
})
transaction_id = capture(step1["id"], "transactionId")
merchant_id = capture(step1["merchantId"], "merchantId")
# Phase 2: Capture what you shipped
# Step 2: Capture the authorization when you ship
call("POST", f"/api/transactions/by-merchant/{quote(merchant_id, safe='')}/{quote(transaction_id, safe='')}/operations", {
"operationType": "Capture",
"amount": 9.00
})
# Phase 3: Confirm what was captured
# Step 3: Read the transaction back
call("GET", f"/api/transactions/{quote(transaction_id, safe='')}")
# Phase 4: If the amount can still grow
# Step 4: Raise the authorization rather than authorizing again
# Raise the existing hold rather than adding a second card transaction alongside the first. A
# capture can only go down from the amount you authorized, and an order can still grow before you
# fulfill it: an added item, a tip, an extended stay. That's the IncrementalAuthorization operation
# type, on the same endpoint the capture above used. It sends a fresh authorization message to the
# network, so the issuer can refuse it, and not every processor supports it. The reference below
# documents it in full. This blueprint stops at the ordinary shipped-for-less flow.
Next steps