AI smart setup
Combined analysis — detects page rotations AND extracts form fields in a single call. Scans all documents in the template, not just the first.
For multi-document templates, the AI sees all PDFs in a single request and
returns fields with unique names across documents (e.g., “CFA — Effective Date”
vs “LPOA — Effective Date”). Each field includes a document_index indicating
which document it belongs to, and areas[].attachment_uuid is set to the correct
document UUID. Page numbers are per-document (1-indexed within each document).
The returned fields can be passed directly to PUT /api/templates/:id as the
schema array. Note: smart_setup is read-only — it does not auto-persist the
schema. You must PUT it separately after reviewing/adjusting the detected fields.
Accounts on the v2 detection pipeline receive additional response metadata
(see pipeline property) and richer preferences on each field (canonical
key, per-field confidence, anchor_text, and anchor_method).
Positioning pipeline: The v2 pipeline uses the PDF text layer to provide
exact label and blank-line coordinates to the AI, enabling precise field
placement. Fields are snapped to detected underscore runs (anchor_method: snapped_to_blank) with adaptive sizing based on measured blank dimensions.
A visual verification pass reviews each page with field rectangles overlaid
to catch positioning errors before returning results.
On v2, callers may optionally send context to provide advisory hints and
expected canonical fields; legacy v1 silently ignores it.
curl --request POST \
--url https://spitshake.io/api/templates/{template_id}/ai/smart_setup \
--header 'Content-Type: application/json' \
--header 'X-Auth-Token: <api-key>' \
--data '
{
"detection_mode": "all",
"context": {
"document_hint": "Customs POA; signer is the importer of record.",
"instructions": "Skip the notary section.",
"expected_fields": [
{
"key": "importer_name",
"name": "Importer Name",
"type": "text"
}
],
"merge_mode": "extend",
"per_document": {
"550e8400-e29b-41d4-a716-446655440000": {
"document_hint": "CBP 4811 form"
}
}
}
}
'import requests
url = "https://spitshake.io/api/templates/{template_id}/ai/smart_setup"
payload = {
"detection_mode": "all",
"context": {
"document_hint": "Customs POA; signer is the importer of record.",
"instructions": "Skip the notary section.",
"expected_fields": [
{
"key": "importer_name",
"name": "Importer Name",
"type": "text"
}
],
"merge_mode": "extend",
"per_document": { "550e8400-e29b-41d4-a716-446655440000": { "document_hint": "CBP 4811 form" } }
}
}
headers = {
"X-Auth-Token": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-Auth-Token': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
detection_mode: 'all',
context: {
document_hint: 'Customs POA; signer is the importer of record.',
instructions: 'Skip the notary section.',
expected_fields: [{key: 'importer_name', name: 'Importer Name', type: 'text'}],
merge_mode: 'extend',
per_document: {'550e8400-e29b-41d4-a716-446655440000': {document_hint: 'CBP 4811 form'}}
}
})
};
fetch('https://spitshake.io/api/templates/{template_id}/ai/smart_setup', 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://spitshake.io/api/templates/{template_id}/ai/smart_setup",
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([
'detection_mode' => 'all',
'context' => [
'document_hint' => 'Customs POA; signer is the importer of record.',
'instructions' => 'Skip the notary section.',
'expected_fields' => [
[
'key' => 'importer_name',
'name' => 'Importer Name',
'type' => 'text'
]
],
'merge_mode' => 'extend',
'per_document' => [
'550e8400-e29b-41d4-a716-446655440000' => [
'document_hint' => 'CBP 4811 form'
]
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Auth-Token: <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://spitshake.io/api/templates/{template_id}/ai/smart_setup"
payload := strings.NewReader("{\n \"detection_mode\": \"all\",\n \"context\": {\n \"document_hint\": \"Customs POA; signer is the importer of record.\",\n \"instructions\": \"Skip the notary section.\",\n \"expected_fields\": [\n {\n \"key\": \"importer_name\",\n \"name\": \"Importer Name\",\n \"type\": \"text\"\n }\n ],\n \"merge_mode\": \"extend\",\n \"per_document\": {\n \"550e8400-e29b-41d4-a716-446655440000\": {\n \"document_hint\": \"CBP 4811 form\"\n }\n }\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Auth-Token", "<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://spitshake.io/api/templates/{template_id}/ai/smart_setup")
.header("X-Auth-Token", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"detection_mode\": \"all\",\n \"context\": {\n \"document_hint\": \"Customs POA; signer is the importer of record.\",\n \"instructions\": \"Skip the notary section.\",\n \"expected_fields\": [\n {\n \"key\": \"importer_name\",\n \"name\": \"Importer Name\",\n \"type\": \"text\"\n }\n ],\n \"merge_mode\": \"extend\",\n \"per_document\": {\n \"550e8400-e29b-41d4-a716-446655440000\": {\n \"document_hint\": \"CBP 4811 form\"\n }\n }\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://spitshake.io/api/templates/{template_id}/ai/smart_setup")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-Auth-Token"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"detection_mode\": \"all\",\n \"context\": {\n \"document_hint\": \"Customs POA; signer is the importer of record.\",\n \"instructions\": \"Skip the notary section.\",\n \"expected_fields\": [\n {\n \"key\": \"importer_name\",\n \"name\": \"Importer Name\",\n \"type\": \"text\"\n }\n ],\n \"merge_mode\": \"extend\",\n \"per_document\": {\n \"550e8400-e29b-41d4-a716-446655440000\": {\n \"document_hint\": \"CBP 4811 form\"\n }\n }\n }\n}"
response = http.request(request)
puts response.read_body{
"document_summary": "<string>",
"pages": [
{
"page_number": 123,
"suggested_rotation": "0"
}
],
"fields": [
{
"uuid": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"type": "text",
"submitter_uuid": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"required": true,
"readonly": true,
"key": "<string>",
"default_value": "<string>",
"placeholder": "<string>",
"pattern": "<string>",
"validation": "ssn",
"options": [
"<string>"
],
"formula": "<string>",
"areas": [
{
"x": 23.07,
"y": 20.35,
"w": 25,
"h": 5,
"page": 1,
"attachment_uuid": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
],
"preferences": {}
}
],
"field_count": 123,
"pipeline": {
"version": "v2",
"request_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"latency_ms": 123,
"anchor_hits": 123,
"anchor_misses": 123,
"corrections_applied": 123,
"critic_ran": true,
"documents_scanned": 123,
"verifier_ran": true
}
}Authorizations
API token generated at Settings > API
Path Parameters
Body
Response
Complete setup data
curl --request POST \
--url https://spitshake.io/api/templates/{template_id}/ai/smart_setup \
--header 'Content-Type: application/json' \
--header 'X-Auth-Token: <api-key>' \
--data '
{
"detection_mode": "all",
"context": {
"document_hint": "Customs POA; signer is the importer of record.",
"instructions": "Skip the notary section.",
"expected_fields": [
{
"key": "importer_name",
"name": "Importer Name",
"type": "text"
}
],
"merge_mode": "extend",
"per_document": {
"550e8400-e29b-41d4-a716-446655440000": {
"document_hint": "CBP 4811 form"
}
}
}
}
'import requests
url = "https://spitshake.io/api/templates/{template_id}/ai/smart_setup"
payload = {
"detection_mode": "all",
"context": {
"document_hint": "Customs POA; signer is the importer of record.",
"instructions": "Skip the notary section.",
"expected_fields": [
{
"key": "importer_name",
"name": "Importer Name",
"type": "text"
}
],
"merge_mode": "extend",
"per_document": { "550e8400-e29b-41d4-a716-446655440000": { "document_hint": "CBP 4811 form" } }
}
}
headers = {
"X-Auth-Token": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-Auth-Token': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
detection_mode: 'all',
context: {
document_hint: 'Customs POA; signer is the importer of record.',
instructions: 'Skip the notary section.',
expected_fields: [{key: 'importer_name', name: 'Importer Name', type: 'text'}],
merge_mode: 'extend',
per_document: {'550e8400-e29b-41d4-a716-446655440000': {document_hint: 'CBP 4811 form'}}
}
})
};
fetch('https://spitshake.io/api/templates/{template_id}/ai/smart_setup', 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://spitshake.io/api/templates/{template_id}/ai/smart_setup",
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([
'detection_mode' => 'all',
'context' => [
'document_hint' => 'Customs POA; signer is the importer of record.',
'instructions' => 'Skip the notary section.',
'expected_fields' => [
[
'key' => 'importer_name',
'name' => 'Importer Name',
'type' => 'text'
]
],
'merge_mode' => 'extend',
'per_document' => [
'550e8400-e29b-41d4-a716-446655440000' => [
'document_hint' => 'CBP 4811 form'
]
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Auth-Token: <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://spitshake.io/api/templates/{template_id}/ai/smart_setup"
payload := strings.NewReader("{\n \"detection_mode\": \"all\",\n \"context\": {\n \"document_hint\": \"Customs POA; signer is the importer of record.\",\n \"instructions\": \"Skip the notary section.\",\n \"expected_fields\": [\n {\n \"key\": \"importer_name\",\n \"name\": \"Importer Name\",\n \"type\": \"text\"\n }\n ],\n \"merge_mode\": \"extend\",\n \"per_document\": {\n \"550e8400-e29b-41d4-a716-446655440000\": {\n \"document_hint\": \"CBP 4811 form\"\n }\n }\n }\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Auth-Token", "<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://spitshake.io/api/templates/{template_id}/ai/smart_setup")
.header("X-Auth-Token", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"detection_mode\": \"all\",\n \"context\": {\n \"document_hint\": \"Customs POA; signer is the importer of record.\",\n \"instructions\": \"Skip the notary section.\",\n \"expected_fields\": [\n {\n \"key\": \"importer_name\",\n \"name\": \"Importer Name\",\n \"type\": \"text\"\n }\n ],\n \"merge_mode\": \"extend\",\n \"per_document\": {\n \"550e8400-e29b-41d4-a716-446655440000\": {\n \"document_hint\": \"CBP 4811 form\"\n }\n }\n }\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://spitshake.io/api/templates/{template_id}/ai/smart_setup")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-Auth-Token"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"detection_mode\": \"all\",\n \"context\": {\n \"document_hint\": \"Customs POA; signer is the importer of record.\",\n \"instructions\": \"Skip the notary section.\",\n \"expected_fields\": [\n {\n \"key\": \"importer_name\",\n \"name\": \"Importer Name\",\n \"type\": \"text\"\n }\n ],\n \"merge_mode\": \"extend\",\n \"per_document\": {\n \"550e8400-e29b-41d4-a716-446655440000\": {\n \"document_hint\": \"CBP 4811 form\"\n }\n }\n }\n}"
response = http.request(request)
puts response.read_body{
"document_summary": "<string>",
"pages": [
{
"page_number": 123,
"suggested_rotation": "0"
}
],
"fields": [
{
"uuid": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "<string>",
"type": "text",
"submitter_uuid": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"required": true,
"readonly": true,
"key": "<string>",
"default_value": "<string>",
"placeholder": "<string>",
"pattern": "<string>",
"validation": "ssn",
"options": [
"<string>"
],
"formula": "<string>",
"areas": [
{
"x": 23.07,
"y": 20.35,
"w": 25,
"h": 5,
"page": 1,
"attachment_uuid": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}
],
"preferences": {}
}
],
"field_count": 123,
"pipeline": {
"version": "v2",
"request_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"latency_ms": 123,
"anchor_hits": 123,
"anchor_misses": 123,
"corrections_applied": 123,
"critic_ran": true,
"documents_scanned": 123,
"verifier_ran": true
}
}
