Working with the API
Jobs
Two hosts, one job. Submit and follow it on the queue; read, cancel and download it on the platform.
A generation is a job. You submit it, it runs without you, and you collect it later.
#Two hosts, one job
The queue host follows the common IN_QUEUE / IN_PROGRESS / COMPLETED queue protocol, so a client
written for it works unchanged (Compatibility). The platform host holds the account's view of the same job. They share every id.
| Host | Does | |
|---|---|---|
POST /<address> | queue.auray.run | Submits. Answers the queue envelope below. |
GET /<address>/requests/{id}/status | queue.auray.run | Where it is: IN_QUEUE, IN_PROGRESS, COMPLETED. Add ?logs=1 for progress lines. |
GET /<address>/requests/{id}/status/stream | queue.auray.run | The same, as server-sent events until it completes. Each stream closes after a few minutes with no final frame; reconnect and keep reading, as the official clients do. |
GET /<address>/requests/{id} | queue.auray.run | The result once COMPLETED. Before then it answers 400 request_in_progress, as that protocol does. |
PUT /<address>/requests/{id}/cancel | queue.auray.run | Asks it to stop: 202 requested, 400 already completed, 404 unknown. |
POST /<address> | auray.run | The same submit, held open until the job settles or 300 seconds pass. |
GET /v1/jobs/{id} | api.auray.ai | The job as the account sees it: five statuses, settled, the charge. |
GET /v1/jobs/{id}/assets | api.auray.ai | Signed download URLs. Metered by the megabyte. |
DELETE /v1/jobs/{id} | api.auray.ai | Cancel, and answer what the job became. |
GET /v1/jobs | api.auray.ai | The account's jobs, newest first. |
{id} is the request_id the submit answered: <product>_<your idempotency key>.
#Five statuses, three states
queued ──▶ running ──▶ succeeded
├─▶ failed
└─▶ cancelled| Status | Queue state | Means |
|---|---|---|
queued | IN_QUEUE | Accepted and waiting. Nothing has started. |
running | IN_PROGRESS | Running now. |
succeeded | COMPLETED | Finished. assets holds the output. |
failed | COMPLETED | Stopped. error_code says why, and the charge is reversed. |
cancelled | COMPLETED | You stopped it. What hadn't been made yet is refunded. |
Only queued and running are open. Once a job reaches any of the other three it never changes
again, and settled: true on the platform read says the charge is final too.
A failed job is COMPLETED, which reads wrong and is right: it has finished queueing and
finished running, and the failure travels in error rather than in the state. A client that loops
until COMPLETED terminates on every outcome. A fourth FAILED state is exactly the change that
would make such a client loop for ever.
#Submitting
curl -X POST https://queue.auray.run/auray-ai/krea-2-turbo/text-to-image \
-H "Authorization: Bearer $AURAY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{ "prompt": "…", "aspect_ratio": "16:9", "resolution": 1024 }'import { randomUUID } from "node:crypto";
import { createClient } from "@auray/client";
const auray = createClient(); // reads AURAY_API_KEY
const job = await auray.queue.submit("auray-ai/krea-2-turbo/text-to-image", {
input: {
"prompt": "…",
"aspect_ratio": "16:9",
"resolution": 1024
},
idempotencyKey: randomUUID(),
});
console.log(job.request_id, job.status);import uuid
from auray_client import Client
auray = Client() # reads AURAY_API_KEY
job = auray.queue.submit(
"auray-ai/krea-2-turbo/text-to-image",
input={
"prompt": "…",
"aspect_ratio": "16:9",
"resolution": 1024
},
idempotency_key=str(uuid.uuid4()),
)
print(job["request_id"], job["status"]){
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "submit_job",
"arguments": {
"address": "auray-ai/krea-2-turbo/text-to-image",
"input": {
"prompt": "…",
"aspect_ratio": "16:9",
"resolution": 1024
}
}
}
}import { randomUUID } from "node:crypto";
const res = await fetch("https://queue.auray.run/auray-ai/krea-2-turbo/text-to-image", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.AURAY_API_KEY}`,
"content-type": "application/json",
"Idempotency-Key": randomUUID(),
},
body: JSON.stringify({
"prompt": "…",
"aspect_ratio": "16:9",
"resolution": 1024
}),
});
console.log(res.status, await res.json());import os, uuid, requests
r = requests.post(
"https://queue.auray.run/auray-ai/krea-2-turbo/text-to-image",
headers={"Authorization": f"Bearer {os.environ['AURAY_API_KEY']}", "Idempotency-Key": str(uuid.uuid4())},
json={
"prompt": "…",
"aspect_ratio": "16:9",
"resolution": 1024
},
)
r.raise_for_status()
print(r.json())package main
import (
"bytes"
"crypto/rand"
"encoding/hex"
"fmt"
"io"
"net/http"
"os"
)
func main() {
body := []byte(`{
"prompt": "…",
"aspect_ratio": "16:9",
"resolution": 1024
}`)
req, err := http.NewRequest("POST", "https://queue.auray.run/auray-ai/krea-2-turbo/text-to-image", bytes.NewReader(body))
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("AURAY_API_KEY"))
req.Header.Set("Content-Type", "application/json")
key := make([]byte, 16)
rand.Read(key)
req.Header.Set("Idempotency-Key", hex.EncodeToString(key))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}<?php
$ch = curl_init("https://queue.auray.run/auray-ai/krea-2-turbo/text-to-image");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("AURAY_API_KEY"),
"Content-Type: application/json",
"Idempotency-Key: " . bin2hex(random_bytes(16)),
],
CURLOPT_POSTFIELDS => json_encode([
"prompt" => "…",
"aspect_ratio" => "16:9",
"resolution" => 1024,
], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $response, PHP_EOL;require "net/http"
require "json"
require "securerandom"
uri = URI("https://queue.auray.run/auray-ai/krea-2-turbo/text-to-image")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{ENV.fetch("AURAY_API_KEY")}"
req["Idempotency-Key"] = SecureRandom.uuid
req["Content-Type"] = "application/json"
req.body = {
"prompt" => "…",
"aspect_ratio" => "16:9",
"resolution" => 1024
}.to_json
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.code, res.bodyimport java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.UUID;
public class Main {
public static void main(String[] args) throws Exception {
String body = """
{
"prompt": "…",
"aspect_ratio": "16:9",
"resolution": 1024
}""";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://queue.auray.run/auray-ai/krea-2-turbo/text-to-image"))
.header("Authorization", "Bearer " + System.getenv("AURAY_API_KEY"))
.header("Idempotency-Key", UUID.randomUUID().toString())
.header("Content-Type", "application/json")
.method("POST", HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode() + " " + response.body());
}
}using System.Net.Http.Headers;
using System.Text;
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("AURAY_API_KEY"));
using var request = new HttpRequestMessage(HttpMethod.Post, "https://queue.auray.run/auray-ai/krea-2-turbo/text-to-image");
request.Headers.Add("Idempotency-Key", Guid.NewGuid().ToString());
var body = """
{
"prompt": "…",
"aspect_ratio": "16:9",
"resolution": 1024
}
""";
request.Content = new StringContent(body, Encoding.UTF8, "application/json");
using var response = await client.SendAsync(request);
Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");You get 200 with the queue envelope straight away. The connection closing does not affect the
job. Quickstart shows the whole body; the fields that matter here are
request_id, status, queue_position (how many are ahead while the platform is full),
credits_charged, credits_quoted, expected_seconds, and the three URLs. A job that waits in the
queue is charged when it starts, not when it is submitted: until then its credits_charged is 0,
and credits_quoted is what it will be charged.
Two options travel as query parameters on the submit: ?webhook=<url> (or ?fal_webhook=) asks
for a signed delivery when the job succeeds, and ?wait=<seconds> holds the request open on the
queue host for up to that long. The synchronous host does the second by default.
#Polling
curl "https://queue.auray.run/auray-ai/krea-2-turbo/text-to-image/requests/photo_docs-mtpb1vnh/status?logs=1" \
-H "Authorization: Bearer $AURAY_API_KEY"import { createClient } from "@auray/client";
const auray = createClient(); // reads AURAY_API_KEY
const status = await auray.queue.status("auray-ai/krea-2-turbo/text-to-image", { requestId: "photo_docs-mtpb1vnh", logs: true });
console.log(status.status);from auray_client import Client
auray = Client() # reads AURAY_API_KEY
status = auray.queue.status("auray-ai/krea-2-turbo/text-to-image", request_id="photo_docs-mtpb1vnh", logs=True)
print(status["status"]){
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "check_job",
"arguments": {
"address": "auray-ai/krea-2-turbo/text-to-image",
"request_id": "photo_docs-mtpb1vnh"
}
}
}const res = await fetch("https://queue.auray.run/auray-ai/krea-2-turbo/text-to-image/requests/photo_docs-mtpb1vnh/status?logs=1", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.AURAY_API_KEY}`,
},
});
console.log(res.status, await res.json());import os, requests
r = requests.get(
"https://queue.auray.run/auray-ai/krea-2-turbo/text-to-image/requests/photo_docs-mtpb1vnh/status?logs=1",
headers={"Authorization": f"Bearer {os.environ['AURAY_API_KEY']}"},
)
r.raise_for_status()
print(r.json())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://queue.auray.run/auray-ai/krea-2-turbo/text-to-image/requests/photo_docs-mtpb1vnh/status?logs=1", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("AURAY_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}<?php
$ch = curl_init("https://queue.auray.run/auray-ai/krea-2-turbo/text-to-image/requests/photo_docs-mtpb1vnh/status?logs=1");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("AURAY_API_KEY"),
],
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $response, PHP_EOL;require "net/http"
uri = URI("https://queue.auray.run/auray-ai/krea-2-turbo/text-to-image/requests/photo_docs-mtpb1vnh/status?logs=1")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer #{ENV.fetch("AURAY_API_KEY")}"
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.code, res.bodyimport java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Main {
public static void main(String[] args) throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://queue.auray.run/auray-ai/krea-2-turbo/text-to-image/requests/photo_docs-mtpb1vnh/status?logs=1"))
.header("Authorization", "Bearer " + System.getenv("AURAY_API_KEY"))
.GET()
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode() + " " + response.body());
}
}using System.Net.Http.Headers;
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("AURAY_API_KEY"));
using var request = new HttpRequestMessage(HttpMethod.Get, "https://queue.auray.run/auray-ai/krea-2-turbo/text-to-image/requests/photo_docs-mtpb1vnh/status?logs=1");
using var response = await client.SendAsync(request);
Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");{
"status": "IN_PROGRESS",
"request_id": "photo_docs-mtpb1vnh",
"job_id": "photo_docs-mtpb1vnh",
"logs": [],
"metrics": {},
"status_url": "…", "response_url": "…", "cancel_url": "…"
}metrics.inference_time appears once the runner has reported how long the work itself took.
Video reports its phase as a log line while it runs.
The platform read is the same job from the account's side, and it tells you how long to wait:
curl https://api.auray.ai/v1/jobs/photo_docs-mtpb1vnh \
-H "Authorization: Bearer $AURAY_API_KEY"import { createClient } from "@auray/client";
const auray = createClient(); // reads AURAY_API_KEY
console.log(await auray.platform.jobs.get("photo_docs-mtpb1vnh"));from auray_client import Client
auray = Client() # reads AURAY_API_KEY
print(auray.platform.jobs.get("photo_docs-mtpb1vnh"))const res = await fetch("https://api.auray.ai/v1/jobs/photo_docs-mtpb1vnh", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.AURAY_API_KEY}`,
},
});
console.log(res.status, await res.json());import os, requests
r = requests.get(
"https://api.auray.ai/v1/jobs/photo_docs-mtpb1vnh",
headers={"Authorization": f"Bearer {os.environ['AURAY_API_KEY']}"},
)
r.raise_for_status()
print(r.json())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.auray.ai/v1/jobs/photo_docs-mtpb1vnh", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("AURAY_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}<?php
$ch = curl_init("https://api.auray.ai/v1/jobs/photo_docs-mtpb1vnh");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("AURAY_API_KEY"),
],
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $response, PHP_EOL;require "net/http"
uri = URI("https://api.auray.ai/v1/jobs/photo_docs-mtpb1vnh")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer #{ENV.fetch("AURAY_API_KEY")}"
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.code, res.bodyimport java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Main {
public static void main(String[] args) throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.auray.ai/v1/jobs/photo_docs-mtpb1vnh"))
.header("Authorization", "Bearer " + System.getenv("AURAY_API_KEY"))
.GET()
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode() + " " + response.body());
}
}using System.Net.Http.Headers;
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("AURAY_API_KEY"));
using var request = new HttpRequestMessage(HttpMethod.Get, "https://api.auray.ai/v1/jobs/photo_docs-mtpb1vnh");
using var response = await client.SendAsync(request);
Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");{
"id": "photo_docs-mtpb1vnh",
"job_id": "photo_docs-mtpb1vnh",
"object": "job",
"product": "photo",
"model": "auray-ai/krea-2-turbo/text-to-image",
"engine": "Krea 2 Turbo",
"status": "succeeded",
"settled": true,
"provider_job_id": "f30c5f765770468097181c2bcb31fd4d",
"credits_charged": 4,
"credits_reserved": 4,
"created_at": "2026-09-06T04:21:58.245391+00:00",
"error": null,
"error_code": null,
"assets": [
{ "index": 0, "kind": "image", "key": "photo/krea-2-turbo/f30c5f765770468097181c2bcb31fd4d/0.png", "width": 1344, "height": 768, "bytes": 1065131 }
],
"request_id": "fd85461a-706a-4c71-9d0d-5391319b442a"
}While a job is open the read carries poll_after_seconds; honour it. Reads are rate-limited on
their own bucket, and a 429 carries Retry-After. Both RateLimit-* and X-RateLimit-*
headers say where you stand on every answer.
For anything slower than a few seconds, a webhook is better: you get told once, at the moment it settles, instead of asking repeatedly.
#Collecting the output
The job's assets name what was made. The signed URLs to download it come from one route, on
the platform host, because signing them is metered against the egress bucket and a status poll
is not. The bucket counts the signing, not the fetch: a signed link can be fetched again until it
expires, and asking for the links again counts their size again:
curl https://api.auray.ai/v1/jobs/photo_docs-mtpb1vnh/assets \
-H "Authorization: Bearer $AURAY_API_KEY"import { createClient } from "@auray/client";
const auray = createClient(); // reads AURAY_API_KEY
const { assets } = await auray.platform.jobs.assets("photo_docs-mtpb1vnh");
for (const asset of assets) console.log(asset.url);from auray_client import Client
auray = Client() # reads AURAY_API_KEY
listing = auray.platform.jobs.assets("photo_docs-mtpb1vnh")
for asset in listing["assets"]:
print(asset["url"]){
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "get_job_result",
"arguments": {
"address": "auray-ai/krea-2-turbo/text-to-image",
"request_id": "photo_docs-mtpb1vnh",
"assets": true
}
}
}const res = await fetch("https://api.auray.ai/v1/jobs/photo_docs-mtpb1vnh/assets", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.AURAY_API_KEY}`,
},
});
console.log(res.status, await res.json());import os, requests
r = requests.get(
"https://api.auray.ai/v1/jobs/photo_docs-mtpb1vnh/assets",
headers={"Authorization": f"Bearer {os.environ['AURAY_API_KEY']}"},
)
r.raise_for_status()
print(r.json())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("GET", "https://api.auray.ai/v1/jobs/photo_docs-mtpb1vnh/assets", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("AURAY_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}<?php
$ch = curl_init("https://api.auray.ai/v1/jobs/photo_docs-mtpb1vnh/assets");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "GET",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("AURAY_API_KEY"),
],
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $response, PHP_EOL;require "net/http"
uri = URI("https://api.auray.ai/v1/jobs/photo_docs-mtpb1vnh/assets")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer #{ENV.fetch("AURAY_API_KEY")}"
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.code, res.bodyimport java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Main {
public static void main(String[] args) throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.auray.ai/v1/jobs/photo_docs-mtpb1vnh/assets"))
.header("Authorization", "Bearer " + System.getenv("AURAY_API_KEY"))
.GET()
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode() + " " + response.body());
}
}using System.Net.Http.Headers;
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("AURAY_API_KEY"));
using var request = new HttpRequestMessage(HttpMethod.Get, "https://api.auray.ai/v1/jobs/photo_docs-mtpb1vnh/assets");
using var response = await client.SendAsync(request);
Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");Each asset comes back with filename, content_type, bytes and a url that is good for at most
7 days and never past the file itself — so for what the job has left; the answer's expires_at says when. Download what you intend to keep — treat the
URL as a handle to fetch through, not as permanent storage. Ask GET /v1/jobs/{id}/assets again for fresh links at any
time in the job's 7 days. Fetching assets needs assets:read.
A job is kept 7 days. After that its request and its results are deleted, and both
GET /v1/jobs/{id} and its assets answer 404 not_found — fetch what you need before then. In
the minutes between the end of its 7 days and the sweep that deletes it, its assets answer
410 expired with reason: retention_ended instead: no link is signed past the life of the file
it points at.
#Cancelling
curl -X DELETE https://api.auray.ai/v1/jobs/photo_docs-mtpb1vnh \
-H "Authorization: Bearer $AURAY_API_KEY"import { createClient } from "@auray/client";
const auray = createClient(); // reads AURAY_API_KEY
console.log(await auray.platform.jobs.cancel("photo_docs-mtpb1vnh"));from auray_client import Client
auray = Client() # reads AURAY_API_KEY
print(auray.platform.jobs.cancel("photo_docs-mtpb1vnh"))const res = await fetch("https://api.auray.ai/v1/jobs/photo_docs-mtpb1vnh", {
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.AURAY_API_KEY}`,
},
});
console.log(res.status, await res.json());import os, requests
r = requests.delete(
"https://api.auray.ai/v1/jobs/photo_docs-mtpb1vnh",
headers={"Authorization": f"Bearer {os.environ['AURAY_API_KEY']}"},
)
r.raise_for_status()
print(r.json())package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest("DELETE", "https://api.auray.ai/v1/jobs/photo_docs-mtpb1vnh", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("AURAY_API_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := io.ReadAll(res.Body)
fmt.Println(res.Status, string(out))
}<?php
$ch = curl_init("https://api.auray.ai/v1/jobs/photo_docs-mtpb1vnh");
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => "DELETE",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("AURAY_API_KEY"),
],
]);
$response = curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE), " ", $response, PHP_EOL;require "net/http"
uri = URI("https://api.auray.ai/v1/jobs/photo_docs-mtpb1vnh")
req = Net::HTTP::Delete.new(uri)
req["Authorization"] = "Bearer #{ENV.fetch("AURAY_API_KEY")}"
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.code, res.bodyimport java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Main {
public static void main(String[] args) throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.auray.ai/v1/jobs/photo_docs-mtpb1vnh"))
.header("Authorization", "Bearer " + System.getenv("AURAY_API_KEY"))
.DELETE()
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode() + " " + response.body());
}
}using System.Net.Http.Headers;
using var client = new HttpClient();
client.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("AURAY_API_KEY"));
using var request = new HttpRequestMessage(HttpMethod.Delete, "https://api.auray.ai/v1/jobs/photo_docs-mtpb1vnh");
using var response = await client.SendAsync(request);
Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");The answer says what the job became, whichever way it went:
{
"id": "photo_docs-mtpb1vnh",
"object": "job",
"product": "photo",
"status": "succeeded",
"settled": true,
"cancelled": false,
"credits_charged": 4,
"credits_reserved": 4,
"request_id": "9caaaf7e-d181-4892-90b1-6a7a74032d9a"
}The queue host's PUT …/cancel answers the protocol's three: 202 with CANCELLATION_REQUESTED, 400
already_completed, or 404.
#Money
A refused job is never charged. Anything caught before inference — a bad scope, a malformed body, no credits, a ceiling reached, an address that is not open yet — costs nothing.
A job that fails after starting is refunded automatically. You do not file anything and there is nothing to reconcile.
Every case, product by product — read from the same declaration the refunds are tested against:
| Case | Images | Music | Video | 3D | Catalogue models |
|---|---|---|---|---|---|
| Refused before it starts | Nothing is charged. | Nothing is charged. | Nothing is charged. | Nothing is charged. | Nothing is charged. |
| Waiting for a slot | Nothing is charged until it is dispatched. | Nothing is charged until it is dispatched. | Nothing is charged until it is dispatched. | Nothing is charged until it is dispatched. | No queue: it runs or is refused. |
| Failed | Everything comes back. | Everything comes back. | Everything comes back. | Everything comes back. | Everything comes back. |
| Partly withheld or short | What did not arrive comes back; if nothing arrived, everything does. | What did not arrive comes back; if nothing arrived, everything does. | What did not arrive comes back; if nothing arrived, everything does. | What did not arrive comes back; if nothing arrived, everything does. | What did not arrive comes back; if nothing arrived, everything does. |
| Stopped before it began | Everything comes back. | Everything comes back. | Everything comes back. | Everything comes back until the service starts it; from then — which can be while it still reads queued — the first stage's start-up share is kept. | A job cannot be stopped. |
| Stopped while running | Everything comes back. | The base price is kept, and so is what already ran, rounded down; the rest comes back. | What already ran is kept, rounded down; the rest comes back. | What already ran is kept, rounded down; the rest comes back. | A job cannot be stopped; it finishes and is charged for what it delivers. |
| Stopped responding | Closed 30 minutes after it started, and everything comes back. | Closed 2 hours after it started, and everything comes back. | Closed 210 minutes after it started, and everything comes back. | Closed 2 hours after it started, and everything comes back. | Closed 2 hours after it started, and everything comes back. |
So the only credits you keep paying for are the ones that produced something.
A chat turn — in Messages, a round of Code or the AI Director, /v1/chat and /openai/v1 — is not a job, and settles
by its own rules. A model another company runs keeps writing an answer after it stops reaching you, so on one a turn in
Messages, Playground or the API's chat that you stop, or whose connection drops, after we sent it to the model is finished
and kept, and charged as the finished answer: for its prompt and what it wrote, never more than the credits set aside for
it. A round of Code or the AI Director stopped then, and a turn or a round one of our time limits cuts off — the time a
turn is given, a silence longer than we wait for its next word, or the wait for its first word — is charged in full, for
its prompt and the longest answer it allowed, never more than the credits set aside for it. A turn you stop before we sent
it costs nothing on any model; one that fails for any other reason is charged as any answer is, for its prompt and what
reached you, and nothing if nothing did:
| Case | Our own chat models | Chat models another company runs |
|---|---|---|
| Refused before it writes anything | Nothing is charged. | Only reading what the turn sent the model is charged, at its input rate; no per-turn credit. |
| Failed | If nothing reached you, everything comes back; otherwise what reached you is kept, with its prompt. | If nothing reached you, everything comes back; otherwise what reached you is kept, with its prompt. |
| Stopped by you before we sent it to the model | Everything comes back: nothing had reached the model. | Everything comes back: nothing had reached the model. |
| Stopped by you, or its connection lost, after we sent it to the model — a turn in Messages, Playground or the API's chat | What already reached you is kept, with its prompt; if nothing had, everything comes back. | The answer is finished and kept, and charged as the finished answer: reading what the turn sent the model and what the answer wrote, never more than the credits set aside when it began. |
| Stopped by you, or its connection lost, after we sent it to the model — a round in Code or the AI Director | What already reached you is kept, with its prompt; if nothing had, everything comes back. | Charged in full: reading what the round sent the model and the longest answer the round allowed, however much of it had reached you; never more than the credits set aside when it began. |
| Cut off at one of our time limits — the turn's, a silence, or the wait for its first word | What already reached you is kept, with its prompt; if nothing had, everything comes back. | Charged in full: reading what the turn sent the model and the longest answer the turn — in Code and the AI Director, the round — allowed, however much of it had reached you; never more than the credits set aside when it began. |