Generate a presigned URL for direct CSV upload
curl --request POST \
--url https://app.dograh.com/api/v1/s3/presigned-upload-url \
--header 'Content-Type: application/json' \
--data '
{
"file_name": "<string>",
"file_size": 123,
"content_type": "text/csv"
}
'import requests
url = "https://app.dograh.com/api/v1/s3/presigned-upload-url"
payload = {
"file_name": "<string>",
"file_size": 123,
"content_type": "text/csv"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({file_name: '<string>', file_size: 123, content_type: 'text/csv'})
};
fetch('https://app.dograh.com/api/v1/s3/presigned-upload-url', 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://app.dograh.com/api/v1/s3/presigned-upload-url",
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([
'file_name' => '<string>',
'file_size' => 123,
'content_type' => 'text/csv'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$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://app.dograh.com/api/v1/s3/presigned-upload-url"
payload := strings.NewReader("{\n \"file_name\": \"<string>\",\n \"file_size\": 123,\n \"content_type\": \"text/csv\"\n}")
req, _ := http.NewRequest("POST", url, payload)
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://app.dograh.com/api/v1/s3/presigned-upload-url")
.header("Content-Type", "application/json")
.body("{\n \"file_name\": \"<string>\",\n \"file_size\": 123,\n \"content_type\": \"text/csv\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.dograh.com/api/v1/s3/presigned-upload-url")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"file_name\": \"<string>\",\n \"file_size\": 123,\n \"content_type\": \"text/csv\"\n}"
response = http.request(request)
puts response.read_body{
"upload_url": "<string>",
"file_key": "<string>",
"expires_in": 123
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"input": "<unknown>",
"ctx": {}
}
]
}Campaigns
Upload Contacts CSV
Get a presigned S3 URL to upload a contacts CSV for a campaign
POST
/
api
/
v1
/
s3
/
presigned-upload-url
Generate a presigned URL for direct CSV upload
curl --request POST \
--url https://app.dograh.com/api/v1/s3/presigned-upload-url \
--header 'Content-Type: application/json' \
--data '
{
"file_name": "<string>",
"file_size": 123,
"content_type": "text/csv"
}
'import requests
url = "https://app.dograh.com/api/v1/s3/presigned-upload-url"
payload = {
"file_name": "<string>",
"file_size": 123,
"content_type": "text/csv"
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({file_name: '<string>', file_size: 123, content_type: 'text/csv'})
};
fetch('https://app.dograh.com/api/v1/s3/presigned-upload-url', 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://app.dograh.com/api/v1/s3/presigned-upload-url",
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([
'file_name' => '<string>',
'file_size' => 123,
'content_type' => 'text/csv'
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json"
],
]);
$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://app.dograh.com/api/v1/s3/presigned-upload-url"
payload := strings.NewReader("{\n \"file_name\": \"<string>\",\n \"file_size\": 123,\n \"content_type\": \"text/csv\"\n}")
req, _ := http.NewRequest("POST", url, payload)
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://app.dograh.com/api/v1/s3/presigned-upload-url")
.header("Content-Type", "application/json")
.body("{\n \"file_name\": \"<string>\",\n \"file_size\": 123,\n \"content_type\": \"text/csv\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.dograh.com/api/v1/s3/presigned-upload-url")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"file_name\": \"<string>\",\n \"file_size\": 123,\n \"content_type\": \"text/csv\"\n}"
response = http.request(request)
puts response.read_body{
"upload_url": "<string>",
"file_key": "<string>",
"expires_in": 123
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"input": "<unknown>",
"ctx": {}
}
]
}Uploading contacts is a two-step process. First call this endpoint to get a presigned upload URL, then PUT your CSV directly to that URL.
Use the
To override the Start node greeting for a row, use these optional columns:
# Step 1: Get upload URL
curl -X POST https://your-dograh-instance/api/v1/s3/presigned-upload-url \
-H "X-API-Key: dg_your_api_key"
# Response:
# { "upload_url": "https://...", "s3_key": "campaigns/..." }
# Step 2: Upload the CSV
curl -X PUT "https://presigned-url..." \
-H "Content-Type: text/csv" \
--data-binary @contacts.csv
s3_key from the response as the source_url when creating a campaign.
CSV format
The CSV must include aphone_number column. Any additional columns are passed as initial_context to each call, making them available as template variables in the workflow.
What counts as a valid phone_number depends on the telephony configuration the campaign dials with. Carriers require E.164 — a +, country code, then the number. An Asterisk ARI configuration also accepts extensions, SIP URIs and dial strings, since those are what its PBX reaches. Either way, numbers must be unique within a campaign and cannot contain spaces, commas or ampersands.
phone_number,customer_name,plan,greeting_override_type,greeting_override_text
+14155550100,Jane Smith,premium,text,Hello {{customer_name}}
+14155550101,Bob Jones,basic,text,Welcome back {{customer_name}}
- Text: set
greeting_override_typetotextand put the message ingreeting_override_text. - Recording: set
greeting_override_typetoaudioand put the public recording ID ingreeting_override_recording_id.
Hello {{customer_name}}. Empty override columns preserve the greeting configured in the Start node.Body
application/json