Entity Credit Transfers
curl --request POST \
--url https://api.nozle.app/engine/api/v1/customers/{customer_id}/entities/{entity_id}/credit-allocations \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.nozle.app/engine/api/v1/customers/{customer_id}/entities/{entity_id}/credit-allocations"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.nozle.app/engine/api/v1/customers/{customer_id}/entities/{entity_id}/credit-allocations', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.nozle.app/engine/api/v1/customers/{customer_id}/entities/{entity_id}/credit-allocations"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}require 'uri'
require 'net/http'
url = URI("https://api.nozle.app/engine/api/v1/customers/{customer_id}/entities/{entity_id}/credit-allocations")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.nozle.app/engine/api/v1/customers/{customer_id}/entities/{entity_id}/credit-allocations",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}HttpResponse<String> response = Unirest.post("https://api.nozle.app/engine/api/v1/customers/{customer_id}/entities/{entity_id}/credit-allocations")
.header("Authorization", "Bearer <token>")
.asString();Product Credits
Entity Credit Transfers
Allocate and return provenance-preserving paid top-up credits
POST
/
engine
/
api
/
v1
/
customers
/
{customer_id}
/
entities
/
{entity_id}
/
credit-allocations
Entity Credit Transfers
curl --request POST \
--url https://api.nozle.app/engine/api/v1/customers/{customer_id}/entities/{entity_id}/credit-allocations \
--header 'Authorization: Bearer <token>'import requests
url = "https://api.nozle.app/engine/api/v1/customers/{customer_id}/entities/{entity_id}/credit-allocations"
headers = {"Authorization": "Bearer <token>"}
response = requests.post(url, headers=headers)
print(response.text)const options = {method: 'POST', headers: {Authorization: 'Bearer <token>'}};
fetch('https://api.nozle.app/engine/api/v1/customers/{customer_id}/entities/{entity_id}/credit-allocations', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));package main
import (
"fmt"
"net/http"
"io"
)
func main() {
url := "https://api.nozle.app/engine/api/v1/customers/{customer_id}/entities/{entity_id}/credit-allocations"
req, _ := http.NewRequest("POST", url, nil)
req.Header.Add("Authorization", "Bearer <token>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}require 'uri'
require 'net/http'
url = URI("https://api.nozle.app/engine/api/v1/customers/{customer_id}/entities/{entity_id}/credit-allocations")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
response = http.request(request)
puts response.read_body<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.nozle.app/engine/api/v1/customers/{customer_id}/entities/{entity_id}/credit-allocations",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}HttpResponse<String> response = Unirest.post("https://api.nozle.app/engine/api/v1/customers/{customer_id}/entities/{entity_id}/credit-allocations")
.header("Authorization", "Bearer <token>")
.asString();Base URL:
Return unused allocated value:
Amounts must be positive decimal strings with at most 18 integer digits and 12 fractional digits. Allocation can draw only from active paid customer top-ups. Deallocation returns unused value to each exact parent source; expiry, priority, payment provenance, and conservation remain enforced by PostgreSQL.
Tenant isolation, Entity status, Credit System configuration, source eligibility, and available balance are validated for every transfer.
Retry an uncertain response with the same idempotency key and identical payload. Reusing a key with a different customer, Entity, Credit System, direction, or amount returns
If a request returns HTTP
https://api.nozle.app/engine/api/v1.
Auth: secret key with entity:write. Publishable keys are rejected.
Allocate paid customer top-up value:
POST /customers/workspace_123/entities/user_42/credit-allocations
Authorization: Bearer sk_...
Idempotency-Key: allocate-user-42-100
Content-Type: application/json
{
"credit_system": "ai_credits",
"amount": "100.000000000001"
}
POST /customers/workspace_123/entities/user_42/credit-deallocations
Authorization: Bearer sk_...
Idempotency-Key: deallocate-user-42-25
Content-Type: application/json
{
"credit_system": "ai_credits",
"amount": "25"
}
409 idempotency_key_reused.
Response
A completed allocation returns201, including a replay. A completed deallocation returns 200. The body has transferred, operation_id, customer_id, entity_id, credit_system, direction, amount, available, parent_sources, entity_sources, and replayed. Each source leg has source_id, source_type, scope, amount, before, after, and expires_at.
When there is not enough eligible value, the response is 422 with the same body, transferred: false, and a reason: insufficient_customer_top_up_credits for an allocation, or insufficient_entity_allocated_credits for a deallocation.
Error handling
| Status | Code | Cause |
|---|---|---|
400 | invalid_request | Invalid body, unknown field, invalid amount, or a missing Idempotency-Key or one over 255 bytes. |
404 | customer_not_found, entity_not_found, credit_system_not_found | The named record was not found. |
409 | idempotency_key_reused | The key was used with a different request. |
422 | entity_inactive | Only an active Entity can receive allocated credits. |
422 | credit_system_archived | An archived Credit System cannot receive new allocations. |
503 | credit_transfer_temporarily_unavailable | Retry after Retry-After with the same key and payload. |
403 with credit_transfer_not_enabled, contact support with the request ID to check the transfer configuration. Do not retry it as a transient network failure or report the transfer as successful.