curl --request POST \
--url https://api.rial.io/v1/verifications \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"max_captures": 3,
"expires_in_seconds": 3600,
"context": {
"kind": "damage_claim",
"summary": "Toyota Corolla front bumper"
},
"webhook_url": "https://api.acmeinsurance.com/rial/webhooks",
"metadata": {
"claim_id": "CLM-9912"
}
}
'import requests
url = "https://api.rial.io/v1/verifications"
payload = {
"max_captures": 3,
"expires_in_seconds": 3600,
"context": {
"kind": "damage_claim",
"summary": "Toyota Corolla front bumper"
},
"webhook_url": "https://api.acmeinsurance.com/rial/webhooks",
"metadata": { "claim_id": "CLM-9912" }
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
max_captures: 3,
expires_in_seconds: 3600,
context: {kind: 'damage_claim', summary: 'Toyota Corolla front bumper'},
webhook_url: 'https://api.acmeinsurance.com/rial/webhooks',
metadata: {claim_id: 'CLM-9912'}
})
};
fetch('https://api.rial.io/v1/verifications', 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.rial.io/v1/verifications",
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([
'max_captures' => 3,
'expires_in_seconds' => 3600,
'context' => [
'kind' => 'damage_claim',
'summary' => 'Toyota Corolla front bumper'
],
'webhook_url' => 'https://api.acmeinsurance.com/rial/webhooks',
'metadata' => [
'claim_id' => 'CLM-9912'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$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.rial.io/v1/verifications"
payload := strings.NewReader("{\n \"max_captures\": 3,\n \"expires_in_seconds\": 3600,\n \"context\": {\n \"kind\": \"damage_claim\",\n \"summary\": \"Toyota Corolla front bumper\"\n },\n \"webhook_url\": \"https://api.acmeinsurance.com/rial/webhooks\",\n \"metadata\": {\n \"claim_id\": \"CLM-9912\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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.rial.io/v1/verifications")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"max_captures\": 3,\n \"expires_in_seconds\": 3600,\n \"context\": {\n \"kind\": \"damage_claim\",\n \"summary\": \"Toyota Corolla front bumper\"\n },\n \"webhook_url\": \"https://api.acmeinsurance.com/rial/webhooks\",\n \"metadata\": {\n \"claim_id\": \"CLM-9912\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.rial.io/v1/verifications")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"max_captures\": 3,\n \"expires_in_seconds\": 3600,\n \"context\": {\n \"kind\": \"damage_claim\",\n \"summary\": \"Toyota Corolla front bumper\"\n },\n \"webhook_url\": \"https://api.acmeinsurance.com/rial/webhooks\",\n \"metadata\": {\n \"claim_id\": \"CLM-9912\"\n }\n}"
response = http.request(request)
puts response.read_body{
"id": "vfy_01HXYZABCDEFGHJKMNPQRSTVWX",
"status": "pending",
"capture_url": "https://verify.rial.io/v/vfy_01HXYZABCDEFGHJKMNPQRSTVWX",
"created_at": "2026-09-16T19:06:57.718Z",
"expires_at": "2026-09-16T20:06:57.718Z",
"captures_count": 0
}{
"error": {
"code": "invalid_request",
"message": "Request body failed validation",
"fields": [
{
"path": "config.max_captures",
"message": "must be ≤ 20"
}
]
}
}{
"error": {
"code": "unauthorized",
"message": "Missing or invalid API key"
}
}{
"error": {
"code": "monthly_verification_limit_reached",
"message": "Monthly limit of 1000 verifications reached. Contact Rial to move to the Enterprise plan."
}
}{
"error": {
"code": "too_many_requests",
"message": "Rate limit exceeded for this tenant. Retry after the specified interval."
}
}Create a verification
Create a verification and get the link to send. Its settings are fixed once created; to change them, create another.
curl --request POST \
--url https://api.rial.io/v1/verifications \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"max_captures": 3,
"expires_in_seconds": 3600,
"context": {
"kind": "damage_claim",
"summary": "Toyota Corolla front bumper"
},
"webhook_url": "https://api.acmeinsurance.com/rial/webhooks",
"metadata": {
"claim_id": "CLM-9912"
}
}
'import requests
url = "https://api.rial.io/v1/verifications"
payload = {
"max_captures": 3,
"expires_in_seconds": 3600,
"context": {
"kind": "damage_claim",
"summary": "Toyota Corolla front bumper"
},
"webhook_url": "https://api.acmeinsurance.com/rial/webhooks",
"metadata": { "claim_id": "CLM-9912" }
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
max_captures: 3,
expires_in_seconds: 3600,
context: {kind: 'damage_claim', summary: 'Toyota Corolla front bumper'},
webhook_url: 'https://api.acmeinsurance.com/rial/webhooks',
metadata: {claim_id: 'CLM-9912'}
})
};
fetch('https://api.rial.io/v1/verifications', 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.rial.io/v1/verifications",
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([
'max_captures' => 3,
'expires_in_seconds' => 3600,
'context' => [
'kind' => 'damage_claim',
'summary' => 'Toyota Corolla front bumper'
],
'webhook_url' => 'https://api.acmeinsurance.com/rial/webhooks',
'metadata' => [
'claim_id' => 'CLM-9912'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$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.rial.io/v1/verifications"
payload := strings.NewReader("{\n \"max_captures\": 3,\n \"expires_in_seconds\": 3600,\n \"context\": {\n \"kind\": \"damage_claim\",\n \"summary\": \"Toyota Corolla front bumper\"\n },\n \"webhook_url\": \"https://api.acmeinsurance.com/rial/webhooks\",\n \"metadata\": {\n \"claim_id\": \"CLM-9912\"\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
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.rial.io/v1/verifications")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"max_captures\": 3,\n \"expires_in_seconds\": 3600,\n \"context\": {\n \"kind\": \"damage_claim\",\n \"summary\": \"Toyota Corolla front bumper\"\n },\n \"webhook_url\": \"https://api.acmeinsurance.com/rial/webhooks\",\n \"metadata\": {\n \"claim_id\": \"CLM-9912\"\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.rial.io/v1/verifications")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"max_captures\": 3,\n \"expires_in_seconds\": 3600,\n \"context\": {\n \"kind\": \"damage_claim\",\n \"summary\": \"Toyota Corolla front bumper\"\n },\n \"webhook_url\": \"https://api.acmeinsurance.com/rial/webhooks\",\n \"metadata\": {\n \"claim_id\": \"CLM-9912\"\n }\n}"
response = http.request(request)
puts response.read_body{
"id": "vfy_01HXYZABCDEFGHJKMNPQRSTVWX",
"status": "pending",
"capture_url": "https://verify.rial.io/v/vfy_01HXYZABCDEFGHJKMNPQRSTVWX",
"created_at": "2026-09-16T19:06:57.718Z",
"expires_at": "2026-09-16T20:06:57.718Z",
"captures_count": 0
}{
"error": {
"code": "invalid_request",
"message": "Request body failed validation",
"fields": [
{
"path": "config.max_captures",
"message": "must be ≤ 20"
}
]
}
}{
"error": {
"code": "unauthorized",
"message": "Missing or invalid API key"
}
}{
"error": {
"code": "monthly_verification_limit_reached",
"message": "Monthly limit of 1000 verifications reached. Contact Rial to move to the Enterprise plan."
}
}{
"error": {
"code": "too_many_requests",
"message": "Rate limit exceeded for this tenant. Retry after the specified interval."
}
}Authorizations
Secret API key, created in the dashboard under Settings → API. Send it as Authorization: Bearer rk_secret_… from your server only. Publishable keys (pk_live_…) are for the native SDKs and reach the capture endpoints alone. An unknown key returns 401.
Body
Send steps for guided photos, or max_captures for N anonymous photos (default 1) — never both. Everything else is optional.
Body for POST /v1/verifications. All fields optional — sensible defaults: max_captures: 1, expires_in_seconds: 3600. Every photo gets the screen and AI checks; an audit adds found-online; describing the photo (expected_object, context) turns the context check on. Capture quantity comes as EITHER max_captures (N anonymous captures) OR steps (guided slots with key/description/min/max) — never both. context is what the analysis pipeline cross-references against the capture; metadata is opaque pass-through echoed back on every webhook event.
How many anonymous photos to ask for. Use this or steps, never both. Default 1.
1 <= x <= 20Guided steps in order: photos, text questions, uploads, choices, addresses. Use this or max_captures.
1- Option 1
- Option 2
- Option 3
- Option 4
- Option 5
- Option 6
Show child attributes
Show child attributes
How long the capture link stays open, in seconds. Default 3600 (one hour), max 86400.
60 <= x <= 86400Stamp the delivered photos with a C2PA seal. Default true.
verification (photo taken live, default) or audit (a file that already exists, uploaded through the API).
verification, audit What the photo is supposed to show, for the context check: kind names the thing, summary describes it in one line.
Show child attributes
Show child attributes
HTTPS URL that receives verification.created and verification.completed.
Email that receives the result. null opts this verification out; absent uses your account default.
254es, en Subject of the result email. {variable} placeholders allowed.
1Body of the result email. {variable} placeholders allowed.
1Full plain-text message shown after submitting content. HTML, markdown and variables are not interpreted. Omit on create or send null on PATCH to use the default.
1 - 2000Your own key/value strings, returned with every result and webhook. Example: { "claim_id": "CLM-9912" }.
Show child attributes
Show child attributes
Require a GPS fix on the first photo without comparing it to an address.
How much the phone asks of the person at each shot, from low to high; none runs no capture checks. Absent: none.
none, low, medium, high Street address the photo must be taken at. Turns on location_match.
1 - 300What must appear in the photo, e.g. "car". Turns on object_match.
1 - 200One to six aspects to score from 0 to 10, e.g. ["paint", "tires"]. Turns on condition.
1 - 6 elements1 - 40Structured alternative to expected_location, expected_object and condition_aspects. Send one form or the other.
Show child attributes
Show child attributes
Brand profile shown on the capture page. Absent = your default brand.
2 - 40^[a-z0-9]+(?:-[a-z0-9]+)*$Response
Verification minted. Returns the canonical wire shape including the capture_url the tenant sends to the end-user.
A verification as your API key sees it. verdict appears once analysis finished; object_match, location_match and condition appear when you asked for them and the check ran. Your metadata is not echoed back: keep the id.
^vfy_[0-9A-HJKMNP-TV-Z]{26}$Lifecycle state. pending → first capture flips to partially_captured → analysis sets the verdict and transitions to completed. Terminal: completed, expired, failed. abandoned is a side-branch off pending/partially_captured, reported by the capture screen via POST /v1/verifications/:token/progress when the end user leaves before finishing — NOT terminal: a later capture resurrects the row to partially_captured like any other.
pending, partially_captured, completed, expired, failed, abandoned x >= 0Verdict of a live capture. Branch on rial; signals says why when it is false, and unavailable when a check could not be completed.
- Option 1
- Option 2
Show child attributes
Show child attributes
Where the photo was taken, from the device. Absent for uploaded files and when the device reported no fix.
Show child attributes
Show child attributes
Whether the photo was taken at expected_location. verified within GPS tolerance; no_match somewhere else; ungeocoded when the address or the fix could not be resolved; rejected when the device reported a mock location.
Show child attributes
Show child attributes
Object-check result. Present when an object check was requested globally or on an image step. With step-only configuration the status is the worst step result and expected_object is omitted. Independent of the fraud verdict.
Show child attributes
Show child attributes
Object-check results keyed by image step. Missing capture verdicts yield inconclusive; otherwise each value is the worst context result for the step, ignoring not_applicable when live evidence exists.
Show child attributes
Show child attributes
Condition assessment. Present only when the verification was created with condition_aspects — the free-form tenant-defined aspect names (any language, any domain). score is the one-decimal average of aspect scores; label buckets it (>=7.5 good, >=5 fair, else poor). Independent of the fraud verdict.
Show child attributes
Show child attributes
Analysis of a video-step clip, sampled at one frame per second and run through the same checks a photo gets. Written asynchronously after capture — poll or use webhooks; absent until the worker has run.
Show child attributes
Show child attributes
An earlier verification of yours whose photo matches this one. method: "phash": the perceptual hashes are within threshold (Hamming distance 0–64; 0 is bit-identical) — a re-upload. method: "embedding": the hashes differ but the photos are the same scene by embedding (cosine) and a geometric check confirmed it (inliers) — a screenshot or a crop. Present only when a match was found. It does not change rial: a legitimate re-submission and a recycled photo look the same — your reviewer decides.
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Integrity seal over the verification record including answers. Present only when answers exist and sealing is on (seal !== false). The hash proves the stored answers have not changed — it does NOT claim they are true or sensor-attested; answers_provenance carries that distinction explicitly.
- Option 1
- Option 2
Show child attributes
Show child attributes