Skip to main content
POST
Create an upload

Authorizations

Authorization
string
header
required

OptimalDial API key, prefixed od_live_, sent as Authorization: Bearer .... Mint keys in the in-app developer panel; they are scoped to a single organization.

Body

Requires file plus either phone_column or phone_columns. Supplying both is allowed only when they agree.

file
file
required

CSV file (≤100 MB, must include a header row).

phone_column
string

Name of the column containing phone numbers. Use this OR phone_columns.

phone_columns
string

Comma-separated names of up to 10 columns containing phone numbers, e.g. mobile,direct. Every number is validated and billed separately, so a contact with a number in two mapped columns costs 2 credits (3 at max). The same number appearing in several columns is charged once. Results come back as one row per contact with a {column}_OptimalDial_Status column per mapped column. A column name containing a comma cannot be expressed here — use JSON mode, where this is a real array.

skip_mobile_lookup
boolean
default:false
validation_tier
enum<string>
default:standard

Validation tier. standard charges 1 credit/contact; max is more thorough and charges 1.5 credits/contact (billed as ceil(valid_count × 1.5)).

Available options:
standard,
max
filename_override
string

Override the file's original name.

full_name_column
string

Enables OptimalDial Identity. Name of the column holding the contact's full name. Supply this OR both first_name_column and last_name_column.

first_name_column
string

Enables OptimalDial Identity when supplied together with last_name_column. Supplying only one half returns 400.

last_name_column
string

Enables OptimalDial Identity when supplied together with first_name_column. Supplying only one half returns 400.

Response

Upload created, credits charged, ready for processing.

id
string<uuid>
required
user_id
string<uuid>
required
original_filename
string
required
status
enum<string>
required

cancelled is a legacy value retained so historical uploads still parse — no new upload can enter it.

Available options:
pending_mapping,
validating,
validation_failed,
awaiting_confirmation,
ready_for_processing,
processing,
completed,
failed,
cancelled
created_at
string<date-time>
required
updated_at
string<date-time>
required
organization_id
string<uuid> | null
file_size_bytes
integer | null
valid_row_count
integer | null

Contact rows processed.

valid_number_count
integer | null

Billable phone numbers. Equals valid_row_count when one phone column is mapped; larger when several are. This is the count credits are charged on, so do not derive an invalid count by subtracting it from validation_results.total_rows.

invalid_row_count
integer | null
credits_required
integer | null
credits_charged
integer | null
credits_refunded
integer | null

Credits returned to the organization when OptimalDial refunds an upload.

source
enum<string> | null

web for in-app uploads, api for direct /api/v1/uploads calls, api_pool for uploads synthesized from the Contacts API.

Available options:
web,
api,
api_pool,
null
api_key_id
string<uuid> | null
skip_mobile_lookup
boolean | null
validation_tier
enum<string> | null

Validation tier used for this upload (standard or max).

Available options:
standard,
max,
null
identity_enabled
boolean | null

Whether OptimalDial Identity was requested for this upload, i.e. a name column was mapped at creation.

result_summary
object | null

Distribution of the results, populated once status == "completed". Lets you see the shape of a list without downloading it.

export_count
integer

How many times these results have been downloaded, counting both the web app and this API.

last_exported_at
string<date-time> | null
processed_available
boolean

true once the processed file can be downloaded. Storage keys are deliberately not exposed — use the download endpoints.

processed_filtered_available
boolean

true once the deprecated download/processed-filtered endpoint will succeed, whether from a stored file or generated on demand.

error_message
string | null
exceeded_daily_limit
boolean | null

true if this upload was submitted after your organization had already used its plan's daily processing capacity, measured in phone numbers on a midnight America/New_York day boundary. Advisory only — the upload is accepted and processed in full, and the remainder carries into the next processing day at no extra charge. Before 2026-09-04 this was measured per uploading user and per UTC day.