vicigeeksimple guides
Browse
Data and integrations

Automate tasks · Worked integration guide

VICIdial Call URLs: a durable local receipt lab

Run a complete local producer/receiver exercise with a matching Dispo URL contract, private token, SQLite receipt identity, explicit error branches and a tested receiver restart.

Start the guide

Build a durable local receipt before connecting a trigger

This lab starts with an actual local HTTP event and finishes with one durable SQLite receipt that remains one receipt after a receiver restart. The complete files include the receiver, sender, synthetic event and seven-case self-test. There is no hidden CRM operation.

The optional native Dispo URL uses the same field names and receiver path as the tested sender. Its VICIdial substitution and delivery must be checked separately on an approved lab; the local test does not establish that a dialer event fired.

You should seeThe target outcome is a stored receipt with a stable identity and explicit replay/conflict behavior.

Download the complete local receipt lab (ZIP)

Download all lab instructions

Download receiver.py

Download send-example.py

Download selftest.py

Download the fictional event

Step 1 — Choose Dispo and define the bounded event

For this exercise choose Dispo Call URL on one synthetic campaign. The official reference describes it as a server-triggered request when an agent dispositions a call. Start Call URL excludes manual dial calls; choosing the wrong trigger can produce a silent test with no request.

The single-server fixture identifies an event by source TRAINING_A, call_id from the reviewed uniqueid substitution, and event=dispo. The payload also includes numeric lead_id and the disposition. A different topology or correction policy needs a reviewed identity model before deployment.

One matching producer/receiver contractScroll sideways to compare columns
Receiver parameterNative template valueLocal fictional value
sourceTRAINING_ATRAINING_A
eventdispodispo
call_id--A--uniqueid--B--1700000000.42
lead_id--A--lead_id--B--900001
dispo--A--dispo--B--SALE
tokenprivate generated tokenread from mode-0600 file
Sources for this section

Step 2 — Run the self-test and inspect its real result

Extract the complete ZIP into a private local directory, or save the four Python/JSON files there together. Python 3.8 or later with sqlite3 is sufficient; no package install or credentials for VICIdial are required. The test creates its own temporary private state and only contacts loopback.

The receiver validates the token and exact field set, rejects unexpanded variables, and commits the receipt before acknowledging it. The primary key survives a process restart. A changed payload under the same key returns a conflict rather than overwriting the earlier receipt.

Before you use it
Download receiver.py, selftest.py and the lab notes together. This command starts temporary loopback listeners, not a remote service.
Run the complete local HTTP receipt test
python3 -B selftest.py
Evidence · Local fictional loopback HTTP and SQLite self-test only; no native VICIdial trigger or CRM request. One harmless test label normalized for the privacy filter; results unchanged.

Captured demo response · 2026-11-06T22:50:57.148463+00:00. Any filtering, hidden values or test-only data are identified in the capture label. Select Replay transcript to read the recorded output.

Command output line: python3 -B selftest.py
PASS new event: HTTP 201 stored
PASS identical replay: HTTP 200 duplicate
PASS credential rejection: HTTP 403 rejected-token
PASS missing identity: HTTP 400 missing-or-extra-field
PASS unexpanded variable: HTTP 400 invalid-event
PASS changed same-key event: HTTP 409 conflict
PASS receiver restart: replay remains duplicate; ledger still has one row
PASS 7 loopback HTTP cases; no VICIdial event or CRM request was sent
Success looks like
All seven case checks pass, followed by the summary line, including duplicate-after-restart with exactly one ledger row.
Stop if
A failed assertion means the relevant behavior is unverified. Inspect local Python/sqlite support and the exact failing case; do not bypass it.

You should seeThe stored, duplicate, forbidden, invalid and conflict results were observed through real local HTTP; they are not fabricated VICIdial responses.

Download the observed local test transcript

Step 3 — Complete the persistent two-terminal exercise

Run init once, then keep serve running in terminal A. In terminal B run send-example.py twice and inspect the ledger. Stop and restart only this receiver, repeat the sender, and inspect again. Re-running init creates a different exercise and is not a persistence test.

The sender reads the private token file; it does not print the credential or pass it as a shell argument. The expected first response is 201 stored; the repeat and post-restart responses are 200 duplicate. The ledger contains exactly the one fictional event.

  1. Terminal A: python3 -B receiver.py init, then python3 -B receiver.py serve.
  2. Terminal B, same directory: run python3 -B send-example.py twice, then python3 -B receiver.py inspect.
  3. Stop the owned receiver with Ctrl+C. Restart serve without changing its state directory. Repeat send-example.py and inspect.
Before you use it
Complete the two-terminal exercise using the same call-url-state directory.
Inspect the persistent fictional receipt
python3 -B receiver.py inspect
Evidence · Local SQLite receipt after the downloadable sender submitted one fictional loopback event; identifiers are invented. No native VICIdial trigger or CRM operation.

Captured demo response · 2026-11-06T22:50:57.465840+00:00. Any filtering, hidden values or test-only data are identified in the capture label. Select Replay transcript to read the recorded output.

Command output line: python3 -B receiver.py inspect
[
{
"call_id": "1700000000.42",
"dispo": "SALE",
"event": "dispo",
"lead_id": "900001",
"source": "TRAINING_A"
}
]
Success looks like
One row has TRAINING_A / 1700000000.42 / dispo, lead 900001 and SALE. A duplicate request does not add a row.
Stop if
If the row is missing, inspect which state directory and receiver you used. A changed disposition for the same identity should conflict, not replace the receipt.

You should seeYou can stop the process and replay the same event without losing the receipt identity.

Step 4 — Review the native URL without confusing hosts or credentials

The included native-dispo-url.txt targets exactly /call-event and supplies every field this receiver requires. Replace its token privately only in an approved single-server lab. Because Dispo originates on the VICIdial webserver, 127.0.0.1 means that server, not the workstation where you ran the local exercise.

Before saving the test field, keep its prior value and verify the receiver runs on the intended same host. Confirm actual variable expansion with a synthetic call/disposition and compare the ledger with the independent call record. An unexpanded or empty identity must be rejected. Restore the prior field after this separate test.

The token is a replayable query credential and can appear in stored URLs or upstream logs. The teaching receiver suppresses its own access log, but that does not protect other logs. Do not put CRM credentials, notes, names or phone numbers into the URL. A public deployment requires a reviewed authenticated/TLS ingress; the loopback HTTP server is not that deployment.

Before you use it
Use only the reviewed lab host, install-specific variable reference and a private replacement token. Keep the existing campaign field for recovery.
Native Dispo template — not executed on VICIdial
VARhttp://127.0.0.1:8765/call-event?token=REPLACE_PRIVATELY_WITH_GENERATED_TOKEN&source=TRAINING_A&event=dispo&call_id=--A--uniqueid--B--&lead_id=--A--lead_id--B--&dispo=--A--dispo--B--
Not executed · worksheet or reference text

This sample is a template or reading aid, not a terminal command. There is no output to show.

Success looks like
An approved real disposition produces the same field contract and an independently matched durable receipt. This native trigger result has not been captured for this article.
Stop if
Do not use the local workstation address as if it were the webserver, publish the query token, or accept literal --A-- markers.

Download the matching native Dispo URL template

Know exactly which boundary passed

The demonstrated output is one committed local receipt. It does not establish VICIdial retries, event ordering, exactly-once delivery, a CRM write or arbitrary hardware-failure recovery. The seven-case test specifically establishes the observed process-restart behavior.

If a later worker must update a CRM, use a durable outbox and reconcile ambiguous remote outcomes using a reviewed external identity. Receipt storage and the remote acknowledgement are different state transitions. An in-memory Set or an HTTP 200 alone cannot establish them.

Interpret the receiver responseScroll sideways to compare columns
ResponseMeaningNext action
201 storedReceipt committedInspect the matching ledger row
200 duplicateSame identity and payload already storedNo extra receipt required
409 conflictSame identity, different payloadReview correction/version policy
400 invalidMissing, repeated, extra or unexpanded fieldFix the producer contract
403 rejected tokenNo accepted authenticationCheck private token source; do not bypass
503 ledger unavailableNo successful storage acknowledgementFix storage and reconcile before retrying

Finish with a verified local result and a clean stop

No request in the local ledger: confirm the receiver terminal, loopback port and state directory. Local sender succeeds but native trigger does not: check installed trigger/mode, actual substitutions and the webserver’s route to the receiver. A successful local sender is not a reason to claim the native trigger works.

Stop only the receiver you started. If you separately configured a lab Dispo field, restore its captured prior value. Retain the private receipt ledger for review and retire the test credential before reuse. Do not delete live call records or change unrelated integration fields.

You should seeThe local exercise is complete when one stored row remains one row after identical replay and restart, with rejection/conflict cases passing. Native VICIdial delivery and downstream processing need their own observed checks.

Checks and sources

What was checked

  • Seven real loopback HTTP checks passed, including replay after a receiver restart; the observed transcript is downloadable.
  • The receiver has no CRM action. The native Dispo template is source-reviewed and matches the local field contract, but no native trigger execution is claimed.
  • No native VICIdial Dispo request or completed CRM action was captured. The real demonstrated evidence is the local HTTP/SQLite exercise; a saved URL screenshot would not establish native trigger delivery.

Primary references

Sources

  1. VICIdial Call URL featuresVICIdial · accessed November 6, 2026
  2. Python HTTP server limitsPython · accessed November 6, 2026
  3. Python sqlite3 transaction interfacePython · accessed November 6, 2026