Automatdo  ·  TPV Platform

TPV Order API

Submit verification orders directly to the Automatdo TPV platform and receive results back in real time — no CRM sync required.

Audience Partner engineering teams Version 1.0 Date August 26, 2026 Base URL https://app.automatdo.com
01

How the integration works

Orders are pushed to us before the verification call, not fetched during it. Once an order is in our queue, the customer is either dialed by our verification agent or looked up automatically by phone number when they call in. When the call ends, the result comes back to you.

STEP 1
You submit the order
One POST per closed sale, at the moment it needs TPV.
STEP 2
We run the verification
The AI agent conducts the recorded TPV call using your order data.
STEP 3
You receive the result
Signed webhook with outcome, answers, and recording — plus a pull API for reconciliation.

Because order data is pre-loaded, calls are fast (no live lookups against your systems), verification keeps working even if your systems are briefly down, and there is a complete audit trail of exactly what data each verification used.

02

Authentication

We issue you an API key at onboarding. Keys look like sk_live_…, are shown once at creation, and are scoped to your organization — every order you submit and every result you read is automatically isolated to your account.

Send the key on every request using either header:

Authorization: Bearer sk_live_your_key_here
# — or —
X-API-Key: sk_live_your_key_here
ScopeGrants
tpv:writeSubmitting orders
results:readPulling verification results (section 08)
03

Submit an order

POSThttps://app.automatdo.com/voice/api/tpv/orders/

Accepts a flat JSON object. Four fields are required; address fields are strongly recommended (the verification script confirms the service address with the customer). Any additional fields you include are stored with the order and available to the verification script — this is how job-specific details like square footage and pricing reach the call.

Standard fields

FieldNotes
external_order_idRequiredYour unique ID for this sale. Used for deduplication, versioning, and matching results back to your records.
customer_first_nameRequired
customer_last_nameRequired
customer_phoneRequiredThe number we call. E.164 preferred (+16125551234); common US formats are normalized automatically. Invalid numbers are rejected.
customer_emailOptional
supplier_nameOptionalBusiness name read in the script, e.g. "Acme Home Services".
service_address_line1RecommendedAddress of the job site. State should be a two-letter code (uppercased automatically) — it also determines which state’s compliance rules apply to the call.
service_address_line2Optional
service_address_cityRecommended
service_address_stateRecommended
service_address_zipRecommended

Custom fields

Everything else in the payload is passed through to the verification call, so the script can confirm the specifics of your product or service with the customer. For example, a home-services order might include:

FieldExampleUsed for
product_name"Radiant Barrier Package"The product or package being verified
install_sqft"1500"Installation square footage confirmed on the call
unit_count"2"Number of units included
total_price"4,995.00"Total contract price confirmed with the customer

New custom fields don’t require any change on our side to be stored — but if a new field should be spoken or confirmed during the call, tell us so we can update the script to reference it.

Example request

curl -X POST https://app.automatdo.com/voice/api/tpv/orders/ \
  -H "Authorization: Bearer sk_live_your_key_here" \
  -H "Content-Type: application/json" \
  -d '{
    "external_order_id": "ORD-2026-08451",
    "supplier_name": "Acme Home Services",
    "customer_first_name": "Ryan",
    "customer_last_name": "Smith",
    "customer_phone": "+16125551234",
    "customer_email": "ryan.smith@example.com",
    "service_address_line1": "123 Main Street",
    "service_address_city": "Minneapolis",
    "service_address_state": "MN",
    "service_address_zip": "55401",
    "product_name": "Radiant Barrier Package",
    "install_sqft": "1500",
    "unit_count": "2",
    "total_price": "4,995.00"
  }'

Response — 201 Created

{
  "order_id": "3f8b2c1a-9d4e-4f6a-b7c8-1e2d3f4a5b6c",
  "external_order_id": "ORD-2026-08451",
  "status": "pending",
  "created_at": "2026-08-26T14:32:07.123456+00:00"
}
Store the order_id. Results reference both our order_id and your external_order_id, so either works for matching — but the UUID is guaranteed unique across resubmissions.
04

Resubmission & re-verification

Deduplication is keyed on your external_order_id:

Response when a new version is created — 201 Created

{
  "order_id": "7a1c9e2b-…",          // the new version’s ID
  "external_order_id": "ORD-2026-08451",
  "status": "pending",
  "version": 2,
  "previous_order_id": "3f8b2c1a-…",
  "created_at": "2026-08-27T09:02:11.000000+00:00"
}
05

Errors

StatusMeaningBody
400Validation failed — missing required fields, invalid phone number, malformed JSON, or a duplicate active order.{"error": "Validation failed.", "details": [ … ]}
401Missing or invalid API key.{"error": "Invalid API key."}
403Key is valid but lacks the required scope.{"error": "API key lacks tpv:write scope."}
500Unexpected server error — safe to retry with backoff.{"error": "Failed to create order: …"}

Example 400 body

{
  "error": "Validation failed.",
  "details": [
    "customer_phone must be a valid phone number.",
    "Active order with external_order_id 'ORD-2026-08451' already exists (status: pending)."
  ]
}
06

Results webhook

When a verification call finishes, we POST the result to an HTTPS endpoint you provide. This is the primary way results reach you.

HeaderValue
X-Watson-Eventtpv.verification.completed
X-Watson-TimestampUnix timestamp used in the signature
X-Watson-SignatureHMAC-SHA256 hex digest (section 07)
Content-Typeapplication/json

Respond with any 2xx within a few seconds to acknowledge. Failed deliveries are retried automatically up to 5 times with exponential backoff, so brief outages on your side don’t lose results.

Example payload

{
  "event": "tpv.verification.completed",
  "timestamp": "2026-08-26T15:04:22.518000+00:00",
  "data": {
    "result_id": "b4d1f8c2-…",
    "confirmation_number": "TPV8X2K9M3",
    "order_id": "3f8b2c1a-…",
    "external_order_id": "ORD-2026-08451",
    "outcome": "pass",
    "failure_reason": "",
    "failure_question_id": "",
    "completed_at": "2026-08-26T15:04:20+00:00",

    "customer": {
      "first_name": "Ryan", "last_name": "Smith",
      "phone": "+16125551234", "email": "ryan.smith@example.com"
    },
    "service_address": {
      "line1": "123 Main Street", "line2": "",
      "city": "Minneapolis", "state": "MN", "zip": "55401"
    },

    "answers": [
      { "question_id": "Q1", "normalized": "yes" },
      { "question_id": "Q2", "verbatim": "Ryan Michael Smith" }
      // … one entry per script question
    ],

    "compliance": {
      "status": "pass", "summary": "…", "flags": [],
      "policy_id": "…", "policy_version": 3, "checked_at": "…", "error": null
    },

    "recording": { "url": "https://…", "duration_seconds": 312 },
    "call": { "call_id": 48213, "call_sid": "CA…", "direction": "outbound", "duration_seconds": 318 },

    "contract": { /* energy-market fields — null unless applicable to your vertical */ }
  }
}

Outcomes

OutcomeMeaning
passCustomer verified the sale. confirmation_number is your proof of verification.
failCustomer declined or answered a required question negatively — see failure_reason and failure_question_id.
incompleteCall ended before the script finished.
no_answerCustomer didn’t pick up.
wrong_numberThe person reached wasn’t the customer.
callback_requestedCustomer asked to be called at another time.
do_not_callCustomer asked not to be contacted again.
language_barrierVerification couldn’t proceed in the customer’s language.
By default you receive a webhook for every outcome, including incomplete and no_answer. If you only want final pass/fail results delivered, we can configure that.
07

Verifying webhook signatures

Every webhook is signed with a shared secret we give you at onboarding. The signature is an HMAC-SHA256 hex digest of the string {timestamp}.{raw_body} — the timestamp from the X-Watson-Timestamp header, a literal dot, then the raw request body exactly as received.

Python example

import hmac, hashlib

def verify_watson_webhook(request_body: bytes, headers: dict, secret: str) -> bool:
    timestamp = headers["X-Watson-Timestamp"]
    provided  = headers["X-Watson-Signature"]
    payload   = f"{timestamp}.".encode() + request_body
    expected  = hmac.new(secret.encode(), payload, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, provided)
08

Pulling results on demand

For reconciliation, backfills, or as a fallback if your webhook endpoint was down, results can also be pulled. Requires the results:read scope.

GEThttps://app.automatdo.com/integrations/api/v1/tpv/verifications
Query paramNotes
sinceISO 8601 datetime — only results created after this time
outcomeFilter to one outcome, e.g. pass
deliveredtrue/false — filter by whether the webhook was delivered
limitDefault 50, max 200
offsetPagination offset
curl "https://app.automatdo.com/integrations/api/v1/tpv/verifications?since=2026-08-26T00:00:00Z&outcome=pass" \
  -H "Authorization: Bearer sk_live_your_key_here"

The response contains total, limit, offset, and a results array. A single result can be fetched at GET /integrations/api/v1/tpv/verifications/<result_id>.

09

Order lifecycle

Every order moves through a small set of states:

pending in_progress completed failed expired cancelled

Once an order reaches a dashed (terminal) state, resubmitting the same external_order_id starts a fresh verification as a new version (section 04).

10

Go-live checklist

We provide

  • API key with tpv:write + results:read scopes
  • Webhook signing secret
  • A test window to submit sandbox orders and receive live webhooks

We need from you

  • Your HTTPS webhook endpoint URL
  • Confirmation of the custom field list your script should reference
  • Whether you want webhooks for non-final outcomes (no_answer, incomplete)

Suggested test plan

  • Submit a test order to a known phone number and complete a pass
  • Verify the webhook signature check on your side
  • Exercise a resubmission after a terminal outcome
  • Confirm duplicate-active submissions return 400