curl --request POST \
--url https://api.getdialed.ai/v1/flows/jobs/export \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"batch_id": "batch_55667788",
"status": "rejected"
}
'import requests
url = "https://api.getdialed.ai/v1/flows/jobs/export"
payload = {
"batch_id": "batch_55667788",
"status": "rejected"
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({batch_id: 'batch_55667788', status: 'rejected'})
};
fetch('https://api.getdialed.ai/v1/flows/jobs/export', 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.getdialed.ai/v1/flows/jobs/export",
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([
'batch_id' => 'batch_55667788',
'status' => 'rejected'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <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://api.getdialed.ai/v1/flows/jobs/export"
payload := strings.NewReader("{\n \"batch_id\": \"batch_55667788\",\n \"status\": \"rejected\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<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://api.getdialed.ai/v1/flows/jobs/export")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"batch_id\": \"batch_55667788\",\n \"status\": \"rejected\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.getdialed.ai/v1/flows/jobs/export")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"batch_id\": \"batch_55667788\",\n \"status\": \"rejected\"\n}"
response = http.request(request)
puts response.read_body{
"created_at": "2026-08-11T12:00:00Z",
"download_url": "https://example-bucket.s3.amazonaws.com/...",
"download_url_expires_at": "2026-08-11T12:15:00Z",
"expires_at": "2026-08-18T12:00:00Z",
"id": "exp_0123456789abcdef",
"row_count": 1284,
"status": "ready"
}{
"detail": "Authentication required"
}{
"detail": "An export is already being generated for this account. Wait for it to finish, then request the next one."
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"input": "<unknown>",
"ctx": {}
}
]
}{
"detail": "Rate limit exceeded: 100 per 1 minute"
}{
"detail": "CSV export is not available on this deployment. No export was created — contact support rather than retrying."
}Request a CSV export of your jobs
Request a CSV export of the jobs matching a filter — the same filters as the jobs listing (batch_id, status, record_set_id). The flagship use is exporting the rejected rows of a bulk upload: every row of the file carries the record you originally uploaded alongside its outcome, so you can fix the offending column and re-upload the corrected file as a new record set.
Generation is asynchronous. This endpoint returns immediately with the export’s id and a pending status — it never streams a file. Poll GET /jobs/exports/{export_id} until the status is ready, then use the short-lived download_url it returns.
The file is deleted 7 days after the export is requested, which is what expires_at reports. After that, request the export again to regenerate it from the underlying jobs, for as long as those jobs are inside your retention window.
At least one filter is required (422). One export at a time per account: while an export is pending or generating, a second request returns 409.
curl --request POST \
--url https://api.getdialed.ai/v1/flows/jobs/export \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"batch_id": "batch_55667788",
"status": "rejected"
}
'import requests
url = "https://api.getdialed.ai/v1/flows/jobs/export"
payload = {
"batch_id": "batch_55667788",
"status": "rejected"
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({batch_id: 'batch_55667788', status: 'rejected'})
};
fetch('https://api.getdialed.ai/v1/flows/jobs/export', 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.getdialed.ai/v1/flows/jobs/export",
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([
'batch_id' => 'batch_55667788',
'status' => 'rejected'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <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://api.getdialed.ai/v1/flows/jobs/export"
payload := strings.NewReader("{\n \"batch_id\": \"batch_55667788\",\n \"status\": \"rejected\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<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://api.getdialed.ai/v1/flows/jobs/export")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"batch_id\": \"batch_55667788\",\n \"status\": \"rejected\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.getdialed.ai/v1/flows/jobs/export")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"batch_id\": \"batch_55667788\",\n \"status\": \"rejected\"\n}"
response = http.request(request)
puts response.read_body{
"created_at": "2026-08-11T12:00:00Z",
"download_url": "https://example-bucket.s3.amazonaws.com/...",
"download_url_expires_at": "2026-08-11T12:15:00Z",
"expires_at": "2026-08-18T12:00:00Z",
"id": "exp_0123456789abcdef",
"row_count": 1284,
"status": "ready"
}{
"detail": "Authentication required"
}{
"detail": "An export is already being generated for this account. Wait for it to finish, then request the next one."
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"input": "<unknown>",
"ctx": {}
}
]
}{
"detail": "Rate limit exceeded: 100 per 1 minute"
}{
"detail": "CSV export is not available on this deployment. No export was created — contact support rather than retrying."
}Authorizations
Body
Request a CSV export of your jobs.
Accepts the same filters as the jobs listing. The flagship use is exporting
the rejected rows of one batch: filter by batch_id and the rejected
status, and every row of the file carries the record you originally
uploaded alongside its outcome, so you can correct the offending column and
re-upload the file as a new record set.
Generation is asynchronous. The response comes back immediately with the
export's id and a pending status; poll the export to find out when the file
is ready, then fetch its download URL.
At least one filter is required. An export with no filters would mean "every job in the account", which is not a report — scope it to the batch, status or record set you actually want.
Export only jobs belonging to this batch.
Export only jobs in this status, such as the rejected rows of a bulk upload.
staged, queued, pending, started, needs_reconcile, completed, failed, cancelled, accepted, rejected, unprocessed, unresolved Export only jobs staged from this record set.
Response
Successful Response
An export and, once it is ready, how to download it.
status moves from pending to running while the file is generated, then
to ready — or to failed if the export could not be produced, in which
case error_reason says why. expired means the file has passed its
deletion deadline and is gone.
download_url is a short-lived signed URL that appears once the export is
ready. It expires quickly by design and is re-fetchable: read the export
again whenever you need a fresh URL. The file itself is deleted 7 days after
the export was created; after that, request the export again to regenerate it
from the underlying jobs, for as long as those jobs are inside your retention
window.
Identifier of this export.
Lifecycle state: pending, running, ready, failed, or expired once the file has passed its deletion deadline.
pending, running, ready, failed, expired When the export was requested.
When the generated file is deleted — 7 days after the export was requested.
Number of rows in the generated file. Null until the export is ready.
Why a failed export could not be produced: row_cap_exceeded when the filters matched more rows than one export may contain, source_unavailable when the records were no longer retained, or generation_failed. Null unless the export failed.
Short-lived signed download URL, present once the export is ready. Re-read the export to get a fresh one.
When this download URL stops working. The export itself lives until expires_at.