curl --request POST \
--url https://api.getdialed.ai/v1/platforms/five9/domains/{tenancy_id}/estimate \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"action_id": "five9__configuration_service__add_to_list",
"bucket": "Upload",
"record_count": 1000000
}
'import requests
url = "https://api.getdialed.ai/v1/platforms/five9/domains/{tenancy_id}/estimate"
payload = {
"action_id": "five9__configuration_service__add_to_list",
"bucket": "Upload",
"record_count": 1000000
}
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({
action_id: 'five9__configuration_service__add_to_list',
bucket: 'Upload',
record_count: 1000000
})
};
fetch('https://api.getdialed.ai/v1/platforms/five9/domains/{tenancy_id}/estimate', 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/platforms/five9/domains/{tenancy_id}/estimate",
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([
'action_id' => 'five9__configuration_service__add_to_list',
'bucket' => 'Upload',
'record_count' => 1000000
]),
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/platforms/five9/domains/{tenancy_id}/estimate"
payload := strings.NewReader("{\n \"action_id\": \"five9__configuration_service__add_to_list\",\n \"bucket\": \"Upload\",\n \"record_count\": 1000000\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/platforms/five9/domains/{tenancy_id}/estimate")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"action_id\": \"five9__configuration_service__add_to_list\",\n \"bucket\": \"Upload\",\n \"record_count\": 1000000\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.getdialed.ai/v1/platforms/five9/domains/{tenancy_id}/estimate")
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 \"action_id\": \"five9__configuration_service__add_to_list\",\n \"bucket\": \"Upload\",\n \"record_count\": 1000000\n}"
response = http.request(request)
puts response.read_body{
"as_of": "2026-08-14T18:00:00Z",
"bucket": "Upload",
"calls": 20,
"day_remaining_calls": 1100,
"max_records_per_call": 50000,
"pct_of_remaining_day": 1.8,
"queued_estimated_calls": 0,
"records": 1000000,
"spills_past_day_cap": false
}{
"detail": "Authentication required"
}{
"detail": "Record set not found"
}{
"detail": "This action is not rate limited on this Domain, so there is no provider budget to price it against — name a bucket explicitly, or estimate against an action this Domain meters."
}{
"detail": "Rate limit exceeded: 100 per 1 minute"
}Estimate what an upload will cost against a Domain's budget
Ask what an upload would cost BEFORE committing to it. This endpoint COMMITS NOTHING: no records are ingested, no batch is created, no budget is reserved and nothing is written. It reads this Domain’s current budget and the work already queued against it, and returns the projected cost — how many provider calls the rows come to, how much of today’s remaining budget that is, and whether the work would run past today’s cap.
Give exactly ONE row source: record_set_id for rows you have already uploaded, or record_count for a hypothetical size. Give at least one of bucket or action_id so the estimate knows which provider budget to price against; an action that this Domain does not meter, or that spends more than one of its budgets, is rejected rather than priced against a guess.
A spills_past_day_cap verdict of true does NOT mean the work would be refused. It means the work needs more calls than today’s remaining budget, so the overflow is delivered at your Domain’s permitted rate and continues into the following day. The verdict counts the work already queued ahead of you, which is why a modest upload can read as a small percentage and still be flagged as spilling. pct_of_remaining_day, day_remaining_calls and the spill verdict all read null when the remaining budget cannot be read — a share of an unknown budget is unknown, not zero.
Like every budget surface here, the figures are a live projection of the quota ledger and never a reservation: other credentials on this Domain — and systems outside this platform entirely — can spend the same budget between this call and your upload. Readable by any organization member. Cross-tenant access returns 404, never 403.
curl --request POST \
--url https://api.getdialed.ai/v1/platforms/five9/domains/{tenancy_id}/estimate \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"action_id": "five9__configuration_service__add_to_list",
"bucket": "Upload",
"record_count": 1000000
}
'import requests
url = "https://api.getdialed.ai/v1/platforms/five9/domains/{tenancy_id}/estimate"
payload = {
"action_id": "five9__configuration_service__add_to_list",
"bucket": "Upload",
"record_count": 1000000
}
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({
action_id: 'five9__configuration_service__add_to_list',
bucket: 'Upload',
record_count: 1000000
})
};
fetch('https://api.getdialed.ai/v1/platforms/five9/domains/{tenancy_id}/estimate', 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/platforms/five9/domains/{tenancy_id}/estimate",
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([
'action_id' => 'five9__configuration_service__add_to_list',
'bucket' => 'Upload',
'record_count' => 1000000
]),
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/platforms/five9/domains/{tenancy_id}/estimate"
payload := strings.NewReader("{\n \"action_id\": \"five9__configuration_service__add_to_list\",\n \"bucket\": \"Upload\",\n \"record_count\": 1000000\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/platforms/five9/domains/{tenancy_id}/estimate")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"action_id\": \"five9__configuration_service__add_to_list\",\n \"bucket\": \"Upload\",\n \"record_count\": 1000000\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.getdialed.ai/v1/platforms/five9/domains/{tenancy_id}/estimate")
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 \"action_id\": \"five9__configuration_service__add_to_list\",\n \"bucket\": \"Upload\",\n \"record_count\": 1000000\n}"
response = http.request(request)
puts response.read_body{
"as_of": "2026-08-14T18:00:00Z",
"bucket": "Upload",
"calls": 20,
"day_remaining_calls": 1100,
"max_records_per_call": 50000,
"pct_of_remaining_day": 1.8,
"queued_estimated_calls": 0,
"records": 1000000,
"spills_past_day_cap": false
}{
"detail": "Authentication required"
}{
"detail": "Record set not found"
}{
"detail": "This action is not rate limited on this Domain, so there is no provider budget to price it against — name a bucket explicitly, or estimate against an action this Domain meters."
}{
"detail": "Rate limit exceeded: 100 per 1 minute"
}Authorizations
Path Parameters
Body
Ask what an upload would cost, without committing to it.
Give exactly ONE row source — either record_set_id for rows already
uploaded, or record_count for a hypothetical size — and at least one of
bucket or action_id so the estimate knows which provider budget to price
against. Supplying both row sources, or neither, is rejected: two sources
could disagree and there would be no principled way to choose between them.
Response
Successful Response
What an upload will cost against the Domain's provider budget.
A spill warning is INFORMATIONAL and never refuses a trigger. Uploading more than a day's remaining budget is a supported way to use the platform: the dispatcher paces the overflow into the following day and keeps going. Use this to tell someone what an upload will consume before they commit to it — and if they change their mind after triggering, cancel the batch rather than expecting this estimate to have blocked it.
calls is how many provider calls records rows cost at
max_records_per_call; pct_of_remaining_day is THIS upload's own share of
what is left today, while spills_past_day_cap also counts the work already
queued ahead of it — so a modest upload can read as a small percentage and
still be flagged as spilling because of the backlog in front of it.
day_remaining_calls, pct_of_remaining_day and spills_past_day_cap are
null when the remaining daily budget cannot be read, because a percentage
of an unknown budget is unknown rather than zero.