curl --request POST \
--url https://api.trycarhub.com/internal/callbacks/modal \
--header 'Content-Type: application/json' \
--header 'X-Modal-Signature: <x-modal-signature>' \
--data '
{
"job_id": "job_3ZxK9pQr7TnW",
"output_ref": "s3://carhub-out/2026/08/16/job_3ZxK9pQr7TnW.json",
"units_actual": 1,
"model_version": "plate-reader@2026-08-27",
"error": {
"code": "model_inference_failed",
"message": "<string>"
},
"provider_job_id": "<string>",
"duration_ms": 1
}
'import requests
url = "https://api.trycarhub.com/internal/callbacks/modal"
payload = {
"job_id": "job_3ZxK9pQr7TnW",
"output_ref": "s3://carhub-out/2026/08/16/job_3ZxK9pQr7TnW.json",
"units_actual": 1,
"model_version": "plate-reader@2026-08-27",
"error": {
"code": "model_inference_failed",
"message": "<string>"
},
"provider_job_id": "<string>",
"duration_ms": 1
}
headers = {
"X-Modal-Signature": "<x-modal-signature>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-Modal-Signature': '<x-modal-signature>', 'Content-Type': 'application/json'},
body: JSON.stringify({
job_id: 'job_3ZxK9pQr7TnW',
output_ref: 's3://carhub-out/2026/08/16/job_3ZxK9pQr7TnW.json',
units_actual: 1,
model_version: 'plate-reader@2026-08-27',
error: {code: 'model_inference_failed', message: '<string>'},
provider_job_id: '<string>',
duration_ms: 1
})
};
fetch('https://api.trycarhub.com/internal/callbacks/modal', 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.trycarhub.com/internal/callbacks/modal",
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([
'job_id' => 'job_3ZxK9pQr7TnW',
'output_ref' => 's3://carhub-out/2026/08/16/job_3ZxK9pQr7TnW.json',
'units_actual' => 1,
'model_version' => 'plate-reader@2026-08-27',
'error' => [
'code' => 'model_inference_failed',
'message' => '<string>'
],
'provider_job_id' => '<string>',
'duration_ms' => 1
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Modal-Signature: <x-modal-signature>"
],
]);
$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.trycarhub.com/internal/callbacks/modal"
payload := strings.NewReader("{\n \"job_id\": \"job_3ZxK9pQr7TnW\",\n \"output_ref\": \"s3://carhub-out/2026/08/16/job_3ZxK9pQr7TnW.json\",\n \"units_actual\": 1,\n \"model_version\": \"plate-reader@2026-08-27\",\n \"error\": {\n \"code\": \"model_inference_failed\",\n \"message\": \"<string>\"\n },\n \"provider_job_id\": \"<string>\",\n \"duration_ms\": 1\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Modal-Signature", "<x-modal-signature>")
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.trycarhub.com/internal/callbacks/modal")
.header("X-Modal-Signature", "<x-modal-signature>")
.header("Content-Type", "application/json")
.body("{\n \"job_id\": \"job_3ZxK9pQr7TnW\",\n \"output_ref\": \"s3://carhub-out/2026/08/16/job_3ZxK9pQr7TnW.json\",\n \"units_actual\": 1,\n \"model_version\": \"plate-reader@2026-08-27\",\n \"error\": {\n \"code\": \"model_inference_failed\",\n \"message\": \"<string>\"\n },\n \"provider_job_id\": \"<string>\",\n \"duration_ms\": 1\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.trycarhub.com/internal/callbacks/modal")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-Modal-Signature"] = '<x-modal-signature>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"job_id\": \"job_3ZxK9pQr7TnW\",\n \"output_ref\": \"s3://carhub-out/2026/08/16/job_3ZxK9pQr7TnW.json\",\n \"units_actual\": 1,\n \"model_version\": \"plate-reader@2026-08-27\",\n \"error\": {\n \"code\": \"model_inference_failed\",\n \"message\": \"<string>\"\n },\n \"provider_job_id\": \"<string>\",\n \"duration_ms\": 1\n}"
response = http.request(request)
puts response.read_bodyInference completion callback (internal)
Not part of the public API. Called by the inference plane when a submitted unit of
work settles. Authenticated by an HMAC-SHA256 of the raw body in X-Modal-Signature,
keyed with a shared secret; unsigned or stale (> 5 min skew) calls are rejected with
401.
The callback is what closes the billing loop: units_actual is the real billable count
(the per-detection meter is only knowable here), and the gateway commits that amount and
releases the rest of the reservation in the same transaction. Deliveries are at-least-once
and must be treated as idempotent on (job_id, status).
curl --request POST \
--url https://api.trycarhub.com/internal/callbacks/modal \
--header 'Content-Type: application/json' \
--header 'X-Modal-Signature: <x-modal-signature>' \
--data '
{
"job_id": "job_3ZxK9pQr7TnW",
"output_ref": "s3://carhub-out/2026/08/16/job_3ZxK9pQr7TnW.json",
"units_actual": 1,
"model_version": "plate-reader@2026-08-27",
"error": {
"code": "model_inference_failed",
"message": "<string>"
},
"provider_job_id": "<string>",
"duration_ms": 1
}
'import requests
url = "https://api.trycarhub.com/internal/callbacks/modal"
payload = {
"job_id": "job_3ZxK9pQr7TnW",
"output_ref": "s3://carhub-out/2026/08/16/job_3ZxK9pQr7TnW.json",
"units_actual": 1,
"model_version": "plate-reader@2026-08-27",
"error": {
"code": "model_inference_failed",
"message": "<string>"
},
"provider_job_id": "<string>",
"duration_ms": 1
}
headers = {
"X-Modal-Signature": "<x-modal-signature>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-Modal-Signature': '<x-modal-signature>', 'Content-Type': 'application/json'},
body: JSON.stringify({
job_id: 'job_3ZxK9pQr7TnW',
output_ref: 's3://carhub-out/2026/08/16/job_3ZxK9pQr7TnW.json',
units_actual: 1,
model_version: 'plate-reader@2026-08-27',
error: {code: 'model_inference_failed', message: '<string>'},
provider_job_id: '<string>',
duration_ms: 1
})
};
fetch('https://api.trycarhub.com/internal/callbacks/modal', 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.trycarhub.com/internal/callbacks/modal",
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([
'job_id' => 'job_3ZxK9pQr7TnW',
'output_ref' => 's3://carhub-out/2026/08/16/job_3ZxK9pQr7TnW.json',
'units_actual' => 1,
'model_version' => 'plate-reader@2026-08-27',
'error' => [
'code' => 'model_inference_failed',
'message' => '<string>'
],
'provider_job_id' => '<string>',
'duration_ms' => 1
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-Modal-Signature: <x-modal-signature>"
],
]);
$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.trycarhub.com/internal/callbacks/modal"
payload := strings.NewReader("{\n \"job_id\": \"job_3ZxK9pQr7TnW\",\n \"output_ref\": \"s3://carhub-out/2026/08/16/job_3ZxK9pQr7TnW.json\",\n \"units_actual\": 1,\n \"model_version\": \"plate-reader@2026-08-27\",\n \"error\": {\n \"code\": \"model_inference_failed\",\n \"message\": \"<string>\"\n },\n \"provider_job_id\": \"<string>\",\n \"duration_ms\": 1\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-Modal-Signature", "<x-modal-signature>")
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.trycarhub.com/internal/callbacks/modal")
.header("X-Modal-Signature", "<x-modal-signature>")
.header("Content-Type", "application/json")
.body("{\n \"job_id\": \"job_3ZxK9pQr7TnW\",\n \"output_ref\": \"s3://carhub-out/2026/08/16/job_3ZxK9pQr7TnW.json\",\n \"units_actual\": 1,\n \"model_version\": \"plate-reader@2026-08-27\",\n \"error\": {\n \"code\": \"model_inference_failed\",\n \"message\": \"<string>\"\n },\n \"provider_job_id\": \"<string>\",\n \"duration_ms\": 1\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.trycarhub.com/internal/callbacks/modal")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-Modal-Signature"] = '<x-modal-signature>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"job_id\": \"job_3ZxK9pQr7TnW\",\n \"output_ref\": \"s3://carhub-out/2026/08/16/job_3ZxK9pQr7TnW.json\",\n \"units_actual\": 1,\n \"model_version\": \"plate-reader@2026-08-27\",\n \"error\": {\n \"code\": \"model_inference_failed\",\n \"message\": \"<string>\"\n },\n \"provider_job_id\": \"<string>\",\n \"duration_ms\": 1\n}"
response = http.request(request)
puts response.read_bodyHeaders
t=<epoch>,v1=<hex hmac-sha256 of "<t>.<raw body>">.
Body
Payload posted by the inference plane when a unit of work settles. Internal contract, not part of the public API surface.
Job identifier — job_ followed by a base58 string.
^job_[1-9A-HJ-NP-Za-km-z]{8,32}$"job_3ZxK9pQr7TnW"
Terminal state reached by the inference call.
succeeded, partial, failed Storage key of the serialised result written by the worker. Required when status is succeeded.
"s3://carhub-out/2026/08/16/job_3ZxK9pQr7TnW.json"
Billable units really consumed. Only the worker knows this for the per-detection meter; the gateway commits exactly this amount and releases the rest.
x >= 0Build that produced the output, surfaced on the job.
"plate-reader@2026-08-27"
Populated when status is failed.
Show child attributes
Show child attributes
Identifier on the inference provider's side, for cross-system tracing.
Wall-clock time of the inference call, fed into latency metrics.
x >= 0Response
Callback accepted.