# Set a unique key once per new action; persist it and reuse it for every retry.
: "${REFLECTIONS_KEY:?Set REFLECTIONS_KEY before running this example}"
: "${REFLECTIONS_CONFIRM_STATEMENT_PAYMENT_IDEMPOTENCY_KEY:?Set REFLECTIONS_CONFIRM_STATEMENT_PAYMENT_IDEMPOTENCY_KEY before running this example}"
curl --fail-with-body --max-time 30 -X POST 'https://app.reflections.ai/v1/statements/stmt_5p7r9t1v3x5z7b9d1f3h5k7m9q/payments/pay_sa9z70h0p3p3npjcjv3gfn3330/confirm' \
-H "Authorization: Bearer $REFLECTIONS_KEY" \
-H "Reflections-Version: 2026-10-15" \
-H "Idempotency-Key: $REFLECTIONS_CONFIRM_STATEMENT_PAYMENT_IDEMPOTENCY_KEY" \
-H "Content-Type: application/json" \
--data-raw '{
"received_at": "2026-10-16T09:00:00Z"
}'import { env, exit } from "node:process";
// Set a unique key once per new action; persist it and reuse it for every retry.
const apiKey = env.REFLECTIONS_KEY;
if (!apiKey) throw new Error("Set REFLECTIONS_KEY before running this example");
const idempotencyKey = env.REFLECTIONS_CONFIRM_STATEMENT_PAYMENT_IDEMPOTENCY_KEY;
if (!idempotencyKey) throw new Error("Set REFLECTIONS_CONFIRM_STATEMENT_PAYMENT_IDEMPOTENCY_KEY before running this example");
const response = await fetch("https://app.reflections.ai/v1/statements/stmt_5p7r9t1v3x5z7b9d1f3h5k7m9q/payments/pay_sa9z70h0p3p3npjcjv3gfn3330/confirm", {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Reflections-Version": "2026-10-15",
"Idempotency-Key": idempotencyKey,
"Content-Type": "application/json",
},
body: JSON.stringify({
"received_at": "2026-10-16T09:00:00Z"
}),
signal: AbortSignal.timeout(30_000),
});
const responseBody = await response.text();
if (!response.ok) {
console.error(`HTTP ${response.status}: ${responseBody}`);
exit(1);
}
console.log(responseBody);
import json
import os
from urllib.error import HTTPError
from urllib.request import Request, urlopen
# Set a unique key once per new action; persist it and reuse it for every retry.
apiKey = os.environ["REFLECTIONS_KEY"]
if not apiKey:
raise ValueError("Set REFLECTIONS_KEY before running this example")
idempotencyKey = os.environ["REFLECTIONS_CONFIRM_STATEMENT_PAYMENT_IDEMPOTENCY_KEY"]
if not idempotencyKey:
raise ValueError("Set REFLECTIONS_CONFIRM_STATEMENT_PAYMENT_IDEMPOTENCY_KEY before running this example")
headers = {
"Authorization": f"Bearer {apiKey}",
"Reflections-Version": "2026-10-15",
"Idempotency-Key": idempotencyKey,
"Content-Type": "application/json",
}
data = json.dumps({"received_at": "2026-10-16T09:00:00Z"}).encode("utf-8")
request = Request("https://app.reflections.ai/v1/statements/stmt_5p7r9t1v3x5z7b9d1f3h5k7m9q/payments/pay_sa9z70h0p3p3npjcjv3gfn3330/confirm",
method="POST", headers=headers, data=data)
try:
with urlopen(request, timeout=30) as response:
print(response.read().decode("utf-8"))
except HTTPError as error:
error_body = error.read().decode("utf-8")
raise RuntimeError(f"HTTP {error.code}: {error_body}") from error
package main
import (
"fmt"
"io"
"net/http"
"os"
"strings"
"time"
)
func main() {
// Set a unique key once per new action; persist it and reuse it for every retry.
apiKey := os.Getenv("REFLECTIONS_KEY")
if apiKey == "" {
panic("Set REFLECTIONS_KEY before running this example")
}
idempotencyKey := os.Getenv("REFLECTIONS_CONFIRM_STATEMENT_PAYMENT_IDEMPOTENCY_KEY")
if idempotencyKey == "" {
panic("Set REFLECTIONS_CONFIRM_STATEMENT_PAYMENT_IDEMPOTENCY_KEY before running this example")
}
body := strings.NewReader(`{
"received_at": "2026-10-16T09:00:00Z"
}`)
request, err := http.NewRequest("POST", "https://app.reflections.ai/v1/statements/stmt_5p7r9t1v3x5z7b9d1f3h5k7m9q/payments/pay_sa9z70h0p3p3npjcjv3gfn3330/confirm", body)
if err != nil {
panic(err)
}
request.Header.Set("Authorization", "Bearer " + apiKey)
request.Header.Set("Reflections-Version", "2026-10-15")
request.Header.Set("Idempotency-Key", idempotencyKey)
request.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 30 * time.Second}
response, err := client.Do(request)
if err != nil {
panic(err)
}
defer response.Body.Close()
responseBody, err := io.ReadAll(response.Body)
if err != nil {
panic(err)
}
if response.StatusCode < 200 || response.StatusCode >= 300 {
panic(fmt.Sprintf("HTTP %d: %s", response.StatusCode, responseBody))
}
fmt.Print(string(responseBody))
}
{
"id": "stmt_5p7r9t1v3x5z7b9d1f3h5k7m9q",
"object": "statement",
"livemode": true,
"license": "lic_ba2q8v1m3n7p4r6s9t0wxyhk5z",
"talent": "tal_9t0wxyhk5zba2q8v1m3n7p4r6s",
"licensee": "acct_7p4r6s9t0wxyhk5zba2q8v1m3n",
"status": "paid",
"period": {
"start": "2026-09-01T00:00:00Z",
"end": "2026-10-01T00:00:00Z"
},
"currency": "usd",
"lines": [
{
"description": "Speech in Maya Okafor's voice",
"units": "seconds",
"reported": 41236,
"observed": 41236,
"billable": 41236,
"rate": {
"amount": 2,
"currency": "usd"
},
"amount": 82472
}
],
"amount_due": 82472,
"amount_paid": 82472,
"reconciliation": {
"result": "matched",
"detail": null
},
"payments": [
{
"id": "pay_sa9z70h0p3p3npjcjv3gfn3330",
"amount": 82472,
"paid_at": "2026-10-14T16:30:00Z",
"method": "bank_transfer",
"reference": "WIRE-58214",
"confirmed_at": "2026-10-16T09:00:00Z"
}
],
"due_at": "2026-10-31T00:00:00Z",
"issued_at": "2026-10-08T08:00:00Z",
"document_url": "https://app.reflections.ai/statements/stmt_5p7r9t1v3x5z7b9d1f3h5k7m9q",
"created_at": "2026-09-01T00:00:00Z"
}{
"type": "https://docs.reflections.ai/errors/parameter_invalid",
"title": "A parameter is invalid",
"status": 400,
"code": "parameter_invalid",
"param": "use.territories[1]",
"detail": "\"UK\" is not an ISO 3166-1 alpha-2 code. Use \"GB\".",
"request_id": "req_8f2k4m6p8r0t2v4x6z8b0d2f4h"
}{
"type": "https://docs.reflections.ai/errors/invalid_api_key",
"title": "The API key is not valid",
"status": 401,
"code": "invalid_api_key",
"request_id": "req_8f2k4m6p8r0t2v4x6z8b0d2f4h"
}{
"type": "https://docs.reflections.ai/errors/mandate_required",
"title": "No mandate with the needed power",
"status": 403,
"code": "mandate_required",
"detail": "Your mandate for tal_9t0wxyhk5zba2q8v1m3n7p4r6s does not include review_outputs.",
"request_id": "req_8f2k4m6p8r0t2v4x6z8b0d2f4h"
}{
"type": "https://docs.reflections.ai/errors/resource_not_found",
"title": "No such object",
"status": 404,
"code": "resource_not_found",
"request_id": "req_8f2k4m6p8r0t2v4x6z8b0d2f4h"
}{
"type": "https://docs.reflections.ai/errors/idempotency_key_in_use",
"title": "A request with this key is still running",
"status": 409,
"code": "idempotency_key_in_use",
"detail": "Retry with the same key after a short wait.",
"request_id": "req_8f2k4m6p8r0t2v4x6z8b0d2f4h"
}{
"type": "https://docs.reflections.ai/errors/idempotency_key_reused",
"title": "This key was used for a different request",
"status": 422,
"code": "idempotency_key_reused",
"request_id": "req_8f2k4m6p8r0t2v4x6z8b0d2f4h"
}{
"type": "https://docs.reflections.ai/errors/rate_limited",
"title": "Too many requests",
"status": 429,
"code": "rate_limited",
"request_id": "req_8f2k4m6p8r0t2v4x6z8b0d2f4h"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"code": "invalid_request",
"request_id": "<string>",
"detail": "<string>",
"param": "<string>",
"doc_url": "<string>"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"code": "invalid_request",
"request_id": "<string>",
"detail": "<string>",
"param": "<string>",
"doc_url": "<string>"
}Confirm a payment
Planned contract — unavailable in test and live modes. This operation is a protocol reference, not a callable endpoint. Start with the working sandbox quickstart.
Confirms that a payment the licensee recorded arrived. Needs a mandate with
receive_statements for the talent. The statement becomes paid when confirmed payments
reach amount_due. Confirming a payment twice returns the statement unchanged.
Restricted keys need statements:write.
# Set a unique key once per new action; persist it and reuse it for every retry.
: "${REFLECTIONS_KEY:?Set REFLECTIONS_KEY before running this example}"
: "${REFLECTIONS_CONFIRM_STATEMENT_PAYMENT_IDEMPOTENCY_KEY:?Set REFLECTIONS_CONFIRM_STATEMENT_PAYMENT_IDEMPOTENCY_KEY before running this example}"
curl --fail-with-body --max-time 30 -X POST 'https://app.reflections.ai/v1/statements/stmt_5p7r9t1v3x5z7b9d1f3h5k7m9q/payments/pay_sa9z70h0p3p3npjcjv3gfn3330/confirm' \
-H "Authorization: Bearer $REFLECTIONS_KEY" \
-H "Reflections-Version: 2026-10-15" \
-H "Idempotency-Key: $REFLECTIONS_CONFIRM_STATEMENT_PAYMENT_IDEMPOTENCY_KEY" \
-H "Content-Type: application/json" \
--data-raw '{
"received_at": "2026-10-16T09:00:00Z"
}'import { env, exit } from "node:process";
// Set a unique key once per new action; persist it and reuse it for every retry.
const apiKey = env.REFLECTIONS_KEY;
if (!apiKey) throw new Error("Set REFLECTIONS_KEY before running this example");
const idempotencyKey = env.REFLECTIONS_CONFIRM_STATEMENT_PAYMENT_IDEMPOTENCY_KEY;
if (!idempotencyKey) throw new Error("Set REFLECTIONS_CONFIRM_STATEMENT_PAYMENT_IDEMPOTENCY_KEY before running this example");
const response = await fetch("https://app.reflections.ai/v1/statements/stmt_5p7r9t1v3x5z7b9d1f3h5k7m9q/payments/pay_sa9z70h0p3p3npjcjv3gfn3330/confirm", {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Reflections-Version": "2026-10-15",
"Idempotency-Key": idempotencyKey,
"Content-Type": "application/json",
},
body: JSON.stringify({
"received_at": "2026-10-16T09:00:00Z"
}),
signal: AbortSignal.timeout(30_000),
});
const responseBody = await response.text();
if (!response.ok) {
console.error(`HTTP ${response.status}: ${responseBody}`);
exit(1);
}
console.log(responseBody);
import json
import os
from urllib.error import HTTPError
from urllib.request import Request, urlopen
# Set a unique key once per new action; persist it and reuse it for every retry.
apiKey = os.environ["REFLECTIONS_KEY"]
if not apiKey:
raise ValueError("Set REFLECTIONS_KEY before running this example")
idempotencyKey = os.environ["REFLECTIONS_CONFIRM_STATEMENT_PAYMENT_IDEMPOTENCY_KEY"]
if not idempotencyKey:
raise ValueError("Set REFLECTIONS_CONFIRM_STATEMENT_PAYMENT_IDEMPOTENCY_KEY before running this example")
headers = {
"Authorization": f"Bearer {apiKey}",
"Reflections-Version": "2026-10-15",
"Idempotency-Key": idempotencyKey,
"Content-Type": "application/json",
}
data = json.dumps({"received_at": "2026-10-16T09:00:00Z"}).encode("utf-8")
request = Request("https://app.reflections.ai/v1/statements/stmt_5p7r9t1v3x5z7b9d1f3h5k7m9q/payments/pay_sa9z70h0p3p3npjcjv3gfn3330/confirm",
method="POST", headers=headers, data=data)
try:
with urlopen(request, timeout=30) as response:
print(response.read().decode("utf-8"))
except HTTPError as error:
error_body = error.read().decode("utf-8")
raise RuntimeError(f"HTTP {error.code}: {error_body}") from error
package main
import (
"fmt"
"io"
"net/http"
"os"
"strings"
"time"
)
func main() {
// Set a unique key once per new action; persist it and reuse it for every retry.
apiKey := os.Getenv("REFLECTIONS_KEY")
if apiKey == "" {
panic("Set REFLECTIONS_KEY before running this example")
}
idempotencyKey := os.Getenv("REFLECTIONS_CONFIRM_STATEMENT_PAYMENT_IDEMPOTENCY_KEY")
if idempotencyKey == "" {
panic("Set REFLECTIONS_CONFIRM_STATEMENT_PAYMENT_IDEMPOTENCY_KEY before running this example")
}
body := strings.NewReader(`{
"received_at": "2026-10-16T09:00:00Z"
}`)
request, err := http.NewRequest("POST", "https://app.reflections.ai/v1/statements/stmt_5p7r9t1v3x5z7b9d1f3h5k7m9q/payments/pay_sa9z70h0p3p3npjcjv3gfn3330/confirm", body)
if err != nil {
panic(err)
}
request.Header.Set("Authorization", "Bearer " + apiKey)
request.Header.Set("Reflections-Version", "2026-10-15")
request.Header.Set("Idempotency-Key", idempotencyKey)
request.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 30 * time.Second}
response, err := client.Do(request)
if err != nil {
panic(err)
}
defer response.Body.Close()
responseBody, err := io.ReadAll(response.Body)
if err != nil {
panic(err)
}
if response.StatusCode < 200 || response.StatusCode >= 300 {
panic(fmt.Sprintf("HTTP %d: %s", response.StatusCode, responseBody))
}
fmt.Print(string(responseBody))
}
{
"id": "stmt_5p7r9t1v3x5z7b9d1f3h5k7m9q",
"object": "statement",
"livemode": true,
"license": "lic_ba2q8v1m3n7p4r6s9t0wxyhk5z",
"talent": "tal_9t0wxyhk5zba2q8v1m3n7p4r6s",
"licensee": "acct_7p4r6s9t0wxyhk5zba2q8v1m3n",
"status": "paid",
"period": {
"start": "2026-09-01T00:00:00Z",
"end": "2026-10-01T00:00:00Z"
},
"currency": "usd",
"lines": [
{
"description": "Speech in Maya Okafor's voice",
"units": "seconds",
"reported": 41236,
"observed": 41236,
"billable": 41236,
"rate": {
"amount": 2,
"currency": "usd"
},
"amount": 82472
}
],
"amount_due": 82472,
"amount_paid": 82472,
"reconciliation": {
"result": "matched",
"detail": null
},
"payments": [
{
"id": "pay_sa9z70h0p3p3npjcjv3gfn3330",
"amount": 82472,
"paid_at": "2026-10-14T16:30:00Z",
"method": "bank_transfer",
"reference": "WIRE-58214",
"confirmed_at": "2026-10-16T09:00:00Z"
}
],
"due_at": "2026-10-31T00:00:00Z",
"issued_at": "2026-10-08T08:00:00Z",
"document_url": "https://app.reflections.ai/statements/stmt_5p7r9t1v3x5z7b9d1f3h5k7m9q",
"created_at": "2026-09-01T00:00:00Z"
}{
"type": "https://docs.reflections.ai/errors/parameter_invalid",
"title": "A parameter is invalid",
"status": 400,
"code": "parameter_invalid",
"param": "use.territories[1]",
"detail": "\"UK\" is not an ISO 3166-1 alpha-2 code. Use \"GB\".",
"request_id": "req_8f2k4m6p8r0t2v4x6z8b0d2f4h"
}{
"type": "https://docs.reflections.ai/errors/invalid_api_key",
"title": "The API key is not valid",
"status": 401,
"code": "invalid_api_key",
"request_id": "req_8f2k4m6p8r0t2v4x6z8b0d2f4h"
}{
"type": "https://docs.reflections.ai/errors/mandate_required",
"title": "No mandate with the needed power",
"status": 403,
"code": "mandate_required",
"detail": "Your mandate for tal_9t0wxyhk5zba2q8v1m3n7p4r6s does not include review_outputs.",
"request_id": "req_8f2k4m6p8r0t2v4x6z8b0d2f4h"
}{
"type": "https://docs.reflections.ai/errors/resource_not_found",
"title": "No such object",
"status": 404,
"code": "resource_not_found",
"request_id": "req_8f2k4m6p8r0t2v4x6z8b0d2f4h"
}{
"type": "https://docs.reflections.ai/errors/idempotency_key_in_use",
"title": "A request with this key is still running",
"status": 409,
"code": "idempotency_key_in_use",
"detail": "Retry with the same key after a short wait.",
"request_id": "req_8f2k4m6p8r0t2v4x6z8b0d2f4h"
}{
"type": "https://docs.reflections.ai/errors/idempotency_key_reused",
"title": "This key was used for a different request",
"status": 422,
"code": "idempotency_key_reused",
"request_id": "req_8f2k4m6p8r0t2v4x6z8b0d2f4h"
}{
"type": "https://docs.reflections.ai/errors/rate_limited",
"title": "Too many requests",
"status": 429,
"code": "rate_limited",
"request_id": "req_8f2k4m6p8r0t2v4x6z8b0d2f4h"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"code": "invalid_request",
"request_id": "<string>",
"detail": "<string>",
"param": "<string>",
"doc_url": "<string>"
}{
"type": "<string>",
"title": "<string>",
"status": 123,
"code": "invalid_request",
"request_id": "<string>",
"detail": "<string>",
"param": "<string>",
"doc_url": "<string>"
}Authorizations
Your secret key, rk_test_… or rk_live_…, sent as Authorization: Bearer <key>. Keep it
on your server. Restricted keys carry only the scopes you give them.
Headers
The dated API version. Defaults to the version your account is pinned to.
"2026-10-15"
A unique key for this operation, up to 255 characters. In test mode, POST keys are scoped to your account and mode and bind the endpoint and body for 24 hours. A different endpoint or body with the same key is refused.
Initial live check and generation creation require a nonempty key. Each operation has a
separate account-scoped namespace; retain the same API key, body and idempotency key for
retries within 24 hours of creation. A pending check returns 202 with status: "checking".
Completed checks replay their sealed response; successful generation POSTs replay the
original accepted 202, so use GET for current state. Live verification is a read-only
query that returns a fresh verification ID on each call, without exact response caching.
The broader delegated-account contract also scopes keys to Reflections-Account;
delegated requests and the other planned live POST operations remain unsupported.
255Path Parameters
The statement's ID.
^stmt_[0-9a-hjkmnp-tv-z]{26}$The payment's ID, from the statement's payments.
^pay_[0-9a-hjkmnp-tv-z]{26}$Body
When the payment arrived. Defaults to now.
Response
The statement, with the payment confirmed.
The statement's ID. Record, confirm and dispute payments against it.
^stmt_[0-9a-hjkmnp-tv-z]{26}$Always statement.
"statement"true in live mode, false in test mode, where statements are generated daily.
The license the statement covers. Each statement covers one license for one month.
^lic_[0-9a-hjkmnp-tv-z]{26}$The talent the licensee owes.
^tal_[0-9a-hjkmnp-tv-z]{26}$The account that holds the license and pays the talent's payee.
^acct_[0-9a-hjkmnp-tv-z]{26}$open while the month runs, then issued. paid once confirmed payments reach
amount_due, or disputed while the talent's side disputes it.
open, issued, paid, disputed The month the statement covers.
Show child attributes
Show child attributes
The ISO 4217 code, lowercase, of every amount on the statement.
^[a-z]{3}$One line per unit the license bills.
Show child attributes
Show child attributes
What the licensee owes for the period under the license's compensation, in the
currency's smallest unit.
The total of payments, in the currency's smallest unit.
How the units reported compare with the units Reflections observed.
Show child attributes
Show child attributes
When the statement opened, at the start of its period.
Payments the licensee recorded against the statement, in the order recorded.
Show child attributes
Show child attributes
Present while the talent's side disputes the statement.
Show child attributes
Show child attributes
When payment is due. Null while open.
When the statement was issued, or last reissued after a dispute. Null while open.
The statement as a document a person reads. Null while open.