Skip to main content
POST
/
v1
/
participants
/
{id}
/
balances
/
forfeit
Forfeit participant balance
curl --request POST \
  --url https://api.scrip.dev/v1/participants/{id}/balances/forfeit \
  --header 'Content-Type: application/json' \
  --header 'X-API-Key: <api-key>' \
  --data '
{
  "asset_id": "550e8400-e29b-41d4-a716-446655440001",
  "description": "Hold for pending order #123",
  "program_id": "550e8400-e29b-41d4-a716-446655440000",
  "amount": "100.00",
  "bucket": "AVAILABLE",
  "earned_from": "2024-01-01T00:00:00Z",
  "earned_to": "2024-12-31T23:59:59Z",
  "idempotency_key": "order-123-hold",
  "lot_ids": [
    "550e8400-e29b-41d4-a716-446655440002"
  ],
  "reference_id": "auth_12345"
}
'
import requests

url = "https://api.scrip.dev/v1/participants/{id}/balances/forfeit"

payload = {
"asset_id": "550e8400-e29b-41d4-a716-446655440001",
"description": "Hold for pending order #123",
"program_id": "550e8400-e29b-41d4-a716-446655440000",
"amount": "100.00",
"bucket": "AVAILABLE",
"earned_from": "2024-01-01T00:00:00Z",
"earned_to": "2024-12-31T23:59:59Z",
"idempotency_key": "order-123-hold",
"lot_ids": ["550e8400-e29b-41d4-a716-446655440002"],
"reference_id": "auth_12345"
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.text)
const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
asset_id: '550e8400-e29b-41d4-a716-446655440001',
description: 'Hold for pending order #123',
program_id: '550e8400-e29b-41d4-a716-446655440000',
amount: '100.00',
bucket: 'AVAILABLE',
earned_from: '2024-01-01T00:00:00Z',
earned_to: '2024-12-31T23:59:59Z',
idempotency_key: 'order-123-hold',
lot_ids: ['550e8400-e29b-41d4-a716-446655440002'],
reference_id: 'auth_12345'
})
};

fetch('https://api.scrip.dev/v1/participants/{id}/balances/forfeit', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));
<?php

$curl = curl_init();

curl_setopt_array($curl, [
CURLOPT_URL => "https://api.scrip.dev/v1/participants/{id}/balances/forfeit",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'asset_id' => '550e8400-e29b-41d4-a716-446655440001',
'description' => 'Hold for pending order #123',
'program_id' => '550e8400-e29b-41d4-a716-446655440000',
'amount' => '100.00',
'bucket' => 'AVAILABLE',
'earned_from' => '2024-01-01T00:00:00Z',
'earned_to' => '2024-12-31T23:59:59Z',
'idempotency_key' => 'order-123-hold',
'lot_ids' => [
'550e8400-e29b-41d4-a716-446655440002'
],
'reference_id' => 'auth_12345'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}
package main

import (
"fmt"
"strings"
"net/http"
"io"
)

func main() {

url := "https://api.scrip.dev/v1/participants/{id}/balances/forfeit"

payload := strings.NewReader("{\n \"asset_id\": \"550e8400-e29b-41d4-a716-446655440001\",\n \"description\": \"Hold for pending order #123\",\n \"program_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"amount\": \"100.00\",\n \"bucket\": \"AVAILABLE\",\n \"earned_from\": \"2024-01-01T00:00:00Z\",\n \"earned_to\": \"2024-12-31T23:59:59Z\",\n \"idempotency_key\": \"order-123-hold\",\n \"lot_ids\": [\n \"550e8400-e29b-41d4-a716-446655440002\"\n ],\n \"reference_id\": \"auth_12345\"\n}")

req, _ := http.NewRequest("POST", url, payload)

req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")

res, _ := http.DefaultClient.Do(req)

defer res.Body.Close()
body, _ := io.ReadAll(res.Body)

fmt.Println(string(body))

}
HttpResponse<String> response = Unirest.post("https://api.scrip.dev/v1/participants/{id}/balances/forfeit")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"asset_id\": \"550e8400-e29b-41d4-a716-446655440001\",\n \"description\": \"Hold for pending order #123\",\n \"program_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"amount\": \"100.00\",\n \"bucket\": \"AVAILABLE\",\n \"earned_from\": \"2024-01-01T00:00:00Z\",\n \"earned_to\": \"2024-12-31T23:59:59Z\",\n \"idempotency_key\": \"order-123-hold\",\n \"lot_ids\": [\n \"550e8400-e29b-41d4-a716-446655440002\"\n ],\n \"reference_id\": \"auth_12345\"\n}")
.asString();
require 'uri'
require 'net/http'

url = URI("https://api.scrip.dev/v1/participants/{id}/balances/forfeit")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"asset_id\": \"550e8400-e29b-41d4-a716-446655440001\",\n \"description\": \"Hold for pending order #123\",\n \"program_id\": \"550e8400-e29b-41d4-a716-446655440000\",\n \"amount\": \"100.00\",\n \"bucket\": \"AVAILABLE\",\n \"earned_from\": \"2024-01-01T00:00:00Z\",\n \"earned_to\": \"2024-12-31T23:59:59Z\",\n \"idempotency_key\": \"order-123-hold\",\n \"lot_ids\": [\n \"550e8400-e29b-41d4-a716-446655440002\"\n ],\n \"reference_id\": \"auth_12345\"\n}"

response = http.request(request)
puts response.read_body
{
  "amount_processed": "100.00",
  "asset_id": "550e8400-e29b-41d4-a716-446655440001",
  "balance": {
    "available": "1400.00",
    "deferred": "0.00",
    "held": "200.00"
  },
  "journal_entry_id": "550e8400-e29b-41d4-a716-446655440003",
  "lots_processed": [
    {
      "amount": "50.00",
      "id": "550e8400-e29b-41d4-a716-446655440002"
    }
  ],
  "message": "Processed 100.00",
  "operation": "HOLD",
  "program_id": "550e8400-e29b-41d4-a716-446655440000",
  "reference_id": "auth_12345"
}
{
"code": "bad_request",
"details": {
"expected": "uuid",
"field": "asset_id",
"fields": [
{
"expected": "<unknown>",
"field": "amount",
"message": "This field is required",
"reason": "required",
"received": "-10.00"
}
],
"reason": "invalid",
"received": "not-a-uuid"
},
"message": "Invalid request parameters"
}
{
"code": "unauthorized",
"details": {
"expected": "uuid",
"field": "asset_id",
"fields": [
{
"expected": "<unknown>",
"field": "amount",
"message": "This field is required",
"reason": "required",
"received": "-10.00"
}
],
"reason": "invalid",
"received": "not-a-uuid"
},
"message": "Missing or invalid credentials"
}
{
"code": "forbidden",
"details": {
"expected": "uuid",
"field": "asset_id",
"fields": [
{
"expected": "<unknown>",
"field": "amount",
"message": "This field is required",
"reason": "required",
"received": "-10.00"
}
],
"reason": "invalid",
"received": "not-a-uuid"
},
"message": "Insufficient permissions for this action"
}
{
"code": "not_found",
"details": {
"expected": "uuid",
"field": "asset_id",
"fields": [
{
"expected": "<unknown>",
"field": "amount",
"message": "This field is required",
"reason": "required",
"received": "-10.00"
}
],
"reason": "invalid",
"received": "not-a-uuid"
},
"message": "Resource not found"
}
{
"code": "conflict",
"details": {
"expected": "uuid",
"field": "asset_id",
"fields": [
{
"expected": "<unknown>",
"field": "amount",
"message": "This field is required",
"reason": "required",
"received": "-10.00"
}
],
"reason": "invalid",
"received": "not-a-uuid"
},
"message": "Resource already exists or state conflict"
}
{
"code": "unsupported_media_type",
"details": {
"expected": "uuid",
"field": "asset_id",
"fields": [
{
"expected": "<unknown>",
"field": "amount",
"message": "This field is required",
"reason": "required",
"received": "-10.00"
}
],
"reason": "invalid",
"received": "not-a-uuid"
},
"message": "Content-Type must be application/json"
}
{
"code": "internal_error",
"message": "An internal error occurred"
}
Removes funds from a participant permanently. Forfeited funds are moved to SYSTEM_BREAKAGE and cannot be recovered. Use this for point expiration, policy violations, or cleaning up balances on closed accounts. The bucket field is required and must be AVAILABLE or HELD, specifying which balance bucket to forfeit from. Unlike most balance operations, forfeit is allowed on CLOSED participants. This operation is irreversible. If you need to temporarily restrict funds without destroying them, use the hold endpoint instead.
For usage patterns and examples, see the Balance Operations guide.

Authorizations

X-API-Key
string
header
required

API key passed in the X-API-Key header.

Headers

Idempotency-Key
string

Idempotency key (1-255 printable ASCII chars). Equivalent to the idempotency_key body field; if both are provided they must match. Replays with the same key and identical parameters return the original result; the same key with different parameters returns 409 idempotency_conflict.

Maximum string length: 255

Path Parameters

id
string<uuid>
required

Participant ID

Body

application/json

Forfeit operation details

asset_id
string<uuid>
required

Asset to operate on

Example:

"550e8400-e29b-41d4-a716-446655440001"

description
string
required

Reason for this operation

Required string length: 1 - 500
Example:

"Hold for pending order #123"

program_id
string<uuid>
required

Program this operation belongs to

Example:

"550e8400-e29b-41d4-a716-446655440000"

amount
string

Amount to process. Omit to process the full balance (SIMPLE) or all matching lots (LOT)

Example:

"100.00"

bucket
enum<string>

Which bucket to forfeit from. Required for forfeit; ignored for hold/release

Available options:
AVAILABLE,
HELD
Example:

"AVAILABLE"

earned_from
string<date-time>

Only process lots earned on or after this time (RFC 3339). LOT mode only.

Example:

"2024-01-01T00:00:00Z"

earned_to
string<date-time>

Only process lots earned on or before this time (RFC 3339). LOT mode only. Must be >= earned_from when both are provided.

Example:

"2024-12-31T23:59:59Z"

idempotency_key
string

Client-provided key to dedupe retries. May also be supplied via the Idempotency-Key HTTP header; when both are present they must match. Identical operations with the same key return the original result; a reused key with different payload returns 409 idempotency_conflict.

Required string length: 1 - 255
Example:

"order-123-hold"

lot_ids
string[]

Restrict to specific lots. LOT mode only

Example:
["550e8400-e29b-41d4-a716-446655440002"]
reference_id
string

Correlation ID linking holds to releases. LOT mode only. Hold stamps lots; release filters by reference.

Example:

"auth_12345"

Response

Forfeit processed

amount_processed
string

Total amount processed

Example:

"100.00"

asset_id
string<uuid>

Asset ID

Example:

"550e8400-e29b-41d4-a716-446655440001"

balance
object

Balance after the operation

journal_entry_id
string<uuid>

Ledger journal entry created by this operation

Example:

"550e8400-e29b-41d4-a716-446655440003"

lots_processed
object[]

Individual lots affected. Only present for LOT mode assets

message
string

Confirmation message

Example:

"Processed 100.00"

operation
string

Operation performed

Example:

"HOLD"

program_id
string<uuid>

Program ID

Example:

"550e8400-e29b-41d4-a716-446655440000"

reference_id
string

Correlation ID linking this hold/release (if provided)

Example:

"auth_12345"