curl --request POST \
--url https://api.getdialed.ai/v1/flows/batches/{batch_id}/pause \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"duration_seconds": 3600,
"reason": "Provider maintenance window"
}
'import requests
url = "https://api.getdialed.ai/v1/flows/batches/{batch_id}/pause"
payload = {
"duration_seconds": 3600,
"reason": "Provider maintenance window"
}
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({duration_seconds: 3600, reason: 'Provider maintenance window'})
};
fetch('https://api.getdialed.ai/v1/flows/batches/{batch_id}/pause', 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/batches/{batch_id}/pause",
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([
'duration_seconds' => 3600,
'reason' => 'Provider maintenance window'
]),
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/batches/{batch_id}/pause"
payload := strings.NewReader("{\n \"duration_seconds\": 3600,\n \"reason\": \"Provider maintenance window\"\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/batches/{batch_id}/pause")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"duration_seconds\": 3600,\n \"reason\": \"Provider maintenance window\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.getdialed.ai/v1/flows/batches/{batch_id}/pause")
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 \"duration_seconds\": 3600,\n \"reason\": \"Provider maintenance window\"\n}"
response = http.request(request)
puts response.read_body{
"cause": "admin_request",
"created_at": "2026-07-29T12:00:00Z",
"created_by": "user_11223344",
"expires_at": "2026-07-29T13:00:00Z",
"extensions": [],
"id": "pause_9f2c1a7b3d4e5f60",
"reason": "Provider maintenance window",
"scope": "global",
"status": "active"
}{
"detail": "Authentication required"
}{
"detail": "Admin role required"
}{
"detail": "Batch not found"
}{
"detail": "Cannot pause a completed, failed, or cancelled batch — a terminal batch has nothing left to dispatch."
}{
"detail": "A batch pause holds one batch — lane and bucket scoping belong to the Domain pause endpoints."
}{
"detail": "Rate limit exceeded: 100 per 1 minute"
}Pause dispatch for a batch
Admin only. Holds outbound dispatch for this batch: an auditable pause record is opened, the batch’s queued dispatch work is flagged so the dispatcher skips it, and the batch’s status becomes paused. Nothing is cancelled or deleted — the batch’s workflow, jobs, and executions are untouched, and resume picks up exactly where dispatch stopped.
duration_seconds schedules an automatic resume: once it comes due, the batch’s queued dispatch work is unheld and the batch is restored to the status it held before the pause, exactly as an explicit resume would restore it, with nothing re-sent. That release is applied the next time dispatch reads the hold, so it takes effect shortly after the duration elapses rather than to the second. Omit duration_seconds for an open-ended hold, which is never released automatically and must be resumed explicitly.
Idempotent: pausing an already-paused batch returns the existing hold. A batch in a terminal state (completed, failed, cancelled) cannot be paused (409). Cross-tenant access returns 404, never 403.
curl --request POST \
--url https://api.getdialed.ai/v1/flows/batches/{batch_id}/pause \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"duration_seconds": 3600,
"reason": "Provider maintenance window"
}
'import requests
url = "https://api.getdialed.ai/v1/flows/batches/{batch_id}/pause"
payload = {
"duration_seconds": 3600,
"reason": "Provider maintenance window"
}
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({duration_seconds: 3600, reason: 'Provider maintenance window'})
};
fetch('https://api.getdialed.ai/v1/flows/batches/{batch_id}/pause', 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/batches/{batch_id}/pause",
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([
'duration_seconds' => 3600,
'reason' => 'Provider maintenance window'
]),
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/batches/{batch_id}/pause"
payload := strings.NewReader("{\n \"duration_seconds\": 3600,\n \"reason\": \"Provider maintenance window\"\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/batches/{batch_id}/pause")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"duration_seconds\": 3600,\n \"reason\": \"Provider maintenance window\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.getdialed.ai/v1/flows/batches/{batch_id}/pause")
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 \"duration_seconds\": 3600,\n \"reason\": \"Provider maintenance window\"\n}"
response = http.request(request)
puts response.read_body{
"cause": "admin_request",
"created_at": "2026-07-29T12:00:00Z",
"created_by": "user_11223344",
"expires_at": "2026-07-29T13:00:00Z",
"extensions": [],
"id": "pause_9f2c1a7b3d4e5f60",
"reason": "Provider maintenance window",
"scope": "global",
"status": "active"
}{
"detail": "Authentication required"
}{
"detail": "Admin role required"
}{
"detail": "Batch not found"
}{
"detail": "Cannot pause a completed, failed, or cancelled batch — a terminal batch has nothing left to dispatch."
}{
"detail": "A batch pause holds one batch — lane and bucket scoping belong to the Domain pause endpoints."
}{
"detail": "Rate limit exceeded: 100 per 1 minute"
}Authorizations
Path Parameters
Body
Open a hold on dispatch.
All fields are optional. duration_seconds schedules an automatic
resume; omit it for an open-ended hold that must be resumed explicitly.
reason is a free-text audit note. lane narrows a Domain-scoped pause
to one named dispatch lane and bucket narrows it to one provider rate
bucket — both are validated against the fixed allowed sets.
Response
Successful Response
One hold on dispatch — an admin hold or an automatic backoff.
Both kinds share this one shape, so a single listing shows everything
currently holding dispatch. scope says how much is held and the
matching target field (tenancy_id, lane, bucket, or batch_id)
says exactly what. cause is the machine-readable origin
(admin_request for a human hold, the provider fault tag for an
automatic backoff) and reason is the human note, if any. A released
pause keeps its record: released_at and released_by complete the
audit trail, and extensions lists every time a scheduled resume was
pushed out.