curl --request POST \
--url https://api.rial.io/v1/link-templates \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"slug": "warehouse-intake",
"name": "Warehouse intake",
"mode": "capture",
"steps": [
{
"key": "front",
"type": "image",
"description": "Front of the pallet",
"min": 1,
"max": 1
}
],
"expires_in_seconds": 3600
}
'import requests
url = "https://api.rial.io/v1/link-templates"
payload = {
"slug": "warehouse-intake",
"name": "Warehouse intake",
"mode": "capture",
"steps": [
{
"key": "front",
"type": "image",
"description": "Front of the pallet",
"min": 1,
"max": 1
}
],
"expires_in_seconds": 3600
}
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({
slug: 'warehouse-intake',
name: 'Warehouse intake',
mode: 'capture',
steps: [
{
key: 'front',
type: 'image',
description: 'Front of the pallet',
min: 1,
max: 1
}
],
expires_in_seconds: 3600
})
};
fetch('https://api.rial.io/v1/link-templates', 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/link-templates",
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([
'slug' => 'warehouse-intake',
'name' => 'Warehouse intake',
'mode' => 'capture',
'steps' => [
[
'key' => 'front',
'type' => 'image',
'description' => 'Front of the pallet',
'min' => 1,
'max' => 1
]
],
'expires_in_seconds' => 3600
]),
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/link-templates"
payload := strings.NewReader("{\n \"slug\": \"warehouse-intake\",\n \"name\": \"Warehouse intake\",\n \"mode\": \"capture\",\n \"steps\": [\n {\n \"key\": \"front\",\n \"type\": \"image\",\n \"description\": \"Front of the pallet\",\n \"min\": 1,\n \"max\": 1\n }\n ],\n \"expires_in_seconds\": 3600\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/link-templates")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"slug\": \"warehouse-intake\",\n \"name\": \"Warehouse intake\",\n \"mode\": \"capture\",\n \"steps\": [\n {\n \"key\": \"front\",\n \"type\": \"image\",\n \"description\": \"Front of the pallet\",\n \"min\": 1,\n \"max\": 1\n }\n ],\n \"expires_in_seconds\": 3600\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.rial.io/v1/link-templates")
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 \"slug\": \"warehouse-intake\",\n \"name\": \"Warehouse intake\",\n \"mode\": \"capture\",\n \"steps\": [\n {\n \"key\": \"front\",\n \"type\": \"image\",\n \"description\": \"Front of the pallet\",\n \"min\": 1,\n \"max\": 1\n }\n ],\n \"expires_in_seconds\": 3600\n}"
response = http.request(request)
puts response.read_body{
"slug": "warehouse-intake",
"org_slug": "acme-logistics",
"public_url": "https://verify.rial.io/l/eyJhbGciOiJIUzI1NiJ9.eyJvIjoiYWNtZS1sb2dpc3RpY3MiLCJ0Ijoid2FyZWhvdXNlLWludGFrZSJ9.3YjQ4G9Wf0mJ8s2K",
"name": "Warehouse intake",
"mode": "capture",
"steps": [
{
"key": "front",
"type": "image",
"description": "Front of the pallet",
"min": 1,
"max": 1
}
],
"expires_in_seconds": 3600,
"status": "active",
"started_total": 0,
"started_today": 0,
"version": 1,
"created_at": "2026-07-30T12:00:00.000Z",
"updated_at": "2026-07-30T12:00:00.000Z"
}{
"error": {
"code": "<string>",
"message": "<string>",
"fields": [
{
"path": "<string>",
"message": "<string>"
}
]
}
}{
"error": "slug_taken"
}{
"error": "unknown_brand"
}{
"error": "storage_unavailable"
}Publish a template
Create a template and its permanent link in one call.
curl --request POST \
--url https://api.rial.io/v1/link-templates \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"slug": "warehouse-intake",
"name": "Warehouse intake",
"mode": "capture",
"steps": [
{
"key": "front",
"type": "image",
"description": "Front of the pallet",
"min": 1,
"max": 1
}
],
"expires_in_seconds": 3600
}
'import requests
url = "https://api.rial.io/v1/link-templates"
payload = {
"slug": "warehouse-intake",
"name": "Warehouse intake",
"mode": "capture",
"steps": [
{
"key": "front",
"type": "image",
"description": "Front of the pallet",
"min": 1,
"max": 1
}
],
"expires_in_seconds": 3600
}
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({
slug: 'warehouse-intake',
name: 'Warehouse intake',
mode: 'capture',
steps: [
{
key: 'front',
type: 'image',
description: 'Front of the pallet',
min: 1,
max: 1
}
],
expires_in_seconds: 3600
})
};
fetch('https://api.rial.io/v1/link-templates', 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/link-templates",
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([
'slug' => 'warehouse-intake',
'name' => 'Warehouse intake',
'mode' => 'capture',
'steps' => [
[
'key' => 'front',
'type' => 'image',
'description' => 'Front of the pallet',
'min' => 1,
'max' => 1
]
],
'expires_in_seconds' => 3600
]),
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/link-templates"
payload := strings.NewReader("{\n \"slug\": \"warehouse-intake\",\n \"name\": \"Warehouse intake\",\n \"mode\": \"capture\",\n \"steps\": [\n {\n \"key\": \"front\",\n \"type\": \"image\",\n \"description\": \"Front of the pallet\",\n \"min\": 1,\n \"max\": 1\n }\n ],\n \"expires_in_seconds\": 3600\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/link-templates")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"slug\": \"warehouse-intake\",\n \"name\": \"Warehouse intake\",\n \"mode\": \"capture\",\n \"steps\": [\n {\n \"key\": \"front\",\n \"type\": \"image\",\n \"description\": \"Front of the pallet\",\n \"min\": 1,\n \"max\": 1\n }\n ],\n \"expires_in_seconds\": 3600\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.rial.io/v1/link-templates")
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 \"slug\": \"warehouse-intake\",\n \"name\": \"Warehouse intake\",\n \"mode\": \"capture\",\n \"steps\": [\n {\n \"key\": \"front\",\n \"type\": \"image\",\n \"description\": \"Front of the pallet\",\n \"min\": 1,\n \"max\": 1\n }\n ],\n \"expires_in_seconds\": 3600\n}"
response = http.request(request)
puts response.read_body{
"slug": "warehouse-intake",
"org_slug": "acme-logistics",
"public_url": "https://verify.rial.io/l/eyJhbGciOiJIUzI1NiJ9.eyJvIjoiYWNtZS1sb2dpc3RpY3MiLCJ0Ijoid2FyZWhvdXNlLWludGFrZSJ9.3YjQ4G9Wf0mJ8s2K",
"name": "Warehouse intake",
"mode": "capture",
"steps": [
{
"key": "front",
"type": "image",
"description": "Front of the pallet",
"min": 1,
"max": 1
}
],
"expires_in_seconds": 3600,
"status": "active",
"started_total": 0,
"started_today": 0,
"version": 1,
"created_at": "2026-07-30T12:00:00.000Z",
"updated_at": "2026-07-30T12:00:00.000Z"
}{
"error": {
"code": "<string>",
"message": "<string>",
"fields": [
{
"path": "<string>",
"message": "<string>"
}
]
}
}{
"error": "slug_taken"
}{
"error": "unknown_brand"
}{
"error": "storage_unavailable"
}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
slug, name and steps are required. Everything else is optional.
URL-safe id, unique in your account: lowercase letters, digits and single hyphens.
2 - 40^[a-z0-9]+(?:-[a-z0-9]+)*$Name shown in the dashboard and to the person capturing.
1 - 80Steps every person goes through, in order.
1- Option 1
- Option 2
- Option 3
- Option 4
- Option 5
- Option 6
Show child attributes
Show child attributes
capture (live photos, default), audit (uploaded files) or hybrid.
capture, audit, hybrid How long each verification minted from the link stays open, in seconds. Default 3600.
60 <= x <= 86400active (default) publishes the link. draft only allows simulation.
draft, active Key/value strings stamped on every verification from this template.
Show child attributes
Show child attributes
Street address every photo must be taken at. Turns on location_match.
1 - 300What must appear in the photos, e.g. "car". Turns on object_match.
1 - 200One to six aspects to score from 0 to 10. 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
Maximum verifications per day. Absent = no cap.
1 <= x <= 10000Maximum verifications in total. Absent = no cap.
1 <= x <= 1000000Brand profile shown on the capture page. Absent = your default brand.
2 - 40^[a-z0-9]+(?:-[a-z0-9]+)*$Email that receives each result. Inherited by every verification from this template.
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 - 2000Which step identifies the person, and how to find their row in the connected database.
Show child attributes
Show child attributes
Connected database: the table and which columns feed the checks.
Show child attributes
Show child attributes
Require a GPS fix on the first photo of every run without comparing it to an address.
How much the phone asks of the person at each shot of every run, from low to high; none runs no capture checks. Absent: none.
none, low, medium, high Response
Template published. version is always 1 on create.
capture, audit, hybrid Show child attributes
Show child attributes
active, paused x >= 1Show child attributes
Show child attributes
es, en Full 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 - 2000Show child attributes
Show child attributes
none, low, medium, high Show child attributes
Show child attributes