Skip to main content

recall-vapi

Caller memory for Vapi voice assistants, backed by Recall by Polign. Loads typed facts before the conversation, saves corrections through a remember Function tool, and remembers them on the next call. Uses the existing polign-recall client and the same starter predicates as recall-livekit; LiveKit is not a dependency. No separate extraction model runs.

Install the adapter with its optional FastAPI webhook router:

pip install 'recall-vapi[fastapi]'

Quick start

See the runnable FastAPI example for inbound phone setup, authentication, a Docker image, and a local two-call check.

from recall_vapi import RecallMemory, RecallVapi, SQLiteState
from recall_vapi.fastapi import create_router

# Once per worker, during application startup:
memory = RecallMemory.open(local_dir="./data/memory", timeout=4)


async def resolve_subject(message):
    # Resolve through your own customer directory. Return None for anonymous
    # callers. Do not use an LLM argument or untrusted browser metadata here.
    return await your_customer_directory.resolve(message["call"])


adapter = RecallVapi(
    memory=memory,
    state=SQLiteState("./data/vapi.sqlite3"),
    assistant=your_base_assistant_configuration,
    tool_server={
        "url": "https://your-service.example/vapi/webhook",
        "headers": {"Authorization": "Bearer " + webhook_token},
    },
    resolve_subject=resolve_subject,
)
app.include_router(create_router(adapter, token=webhook_token))
# During shutdown: await adapter.aclose()

your_customer_directory, the base assistant, token, and FastAPI application above are supplied by your application. The example supplies all of them. Use env={"POLIGN_URL": "https://...", "POLIGN_API_KEY": "..."} instead of local_dir for a shared Polign server. S3/GCS configuration belongs on that server, exactly as in the LiveKit integration. Pass predicates="callers.json" to use your own closed predicate registry.

Vapi configuration

For inbound calls, leave the phone number's assistant unassigned and set its server URL to this webhook. Configure a bearer credential on that saved phone number. Vapi sends assistant-request; the adapter binds the call ID to the resolved subject and returns a transient assistant containing its memory. The saved base assistant is never edited. Vapi requires an assistant response within 7.5 seconds; the router has a six-second handling budget, including bounded resolver and memory reads. Network time is additional. Deploy nearby and measure call setup latency.

The generated tools have an explicit server.headers.Authorization, because Vapi may withhold organization credentials for URLs supplied in a transient configuration. The example also puts this header on the assistant's server for end reports. Keep these configurations on your backend. For browser integrations, register authenticated tools as saved Vapi resources and use their model.toolIds instead of delivering inline credentials to the browser.

Official contracts: server events, Function tools, and server authentication.

Tools and call lifecycle

  • remember(predicate, value) stores or corrects a caller-stated fact. Schemas come from the registry; values are converted to its string/number/boolean type and validated again by Recall. The result includes superseded values.
  • recall(query) searches only the bound caller's memory. Initial loading is capped at 20 facts by default. This is model-triggered search, not LiveKit's automatic per-turn overflow hook.
  • forget(predicate, value) is opt-in with forget_tool=True. It withdraws a specific current fact. History remains; this is not complete data erasure.

Tool arguments cannot choose a customer. Unknown calls, changed bindings, extra arguments, and disabled tools are rejected. An unresolved customer gets the base assistant with no memory tools. A failed initial read continues with an explicit unavailable-memory block. Successful tool results take precedence over the initial prompt; the adapter does not rewrite the live system prompt.

End-of-call reports close bindings but retain duplicate results. Other server events are acknowledged and ignored; transcripts are not stored or extracted. Set the assistant's server URL to this webhook and include end-of-call-report in serverMessages so bindings close. The example does this.

Outbound and web calls

Your authenticated backend can call await adapter.prepare_assistant(subject) before creating a call, then adapter.bind_call(call_id, subject) with the ID Vapi returns. Bind promptly before tools execute: an early tool webhook is rejected until the binding exists. For an outbound production flow that cannot tolerate this race, supply a trusted customer resolver backed by a pre-created call record in your own integration. The included example targets inbound calls.

For web calls, never return the example's transient configuration (it contains the webhook credential) to a browser. Create the call on your backend, or replace inline tools with saved tool IDs before passing configuration to the client. See Vapi web calls.

Retries, concurrency, and persistence

SQLiteState stores call bindings, normalized tool requests, and results. Concurrent duplicate deliveries reserve only one execution. A delayed retry of an old correction returns its old result without changing the newer fact. Reusing an ID with different arguments is rejected. Writes that time out can still finish; their eventual result is recorded for subsequent retries.

This provides at-most-once execution for each (call_id, tool_call_id) while the ledger is retained, not atomic exactly-once delivery across SQLite and Recall. A crash after reservation may leave a pending operation, whether or not Recall committed it. Such operations are never automatically replayed; reconcile the ledger's normalized request against Recall history. Successful remember results include the event ID; the Recall source is user_stated. A new tool-call ID is a new operation.

Keep the SQLite file on persistent local disk. Multiple workers on one host may share it; separate machines need an equivalent shared transactional state implementation with bind, subject, reserve, finish_tool, and finish_call methods. Do not use independent ledgers or put SQLite on NFS. State is retained until you remove it; include its customer IDs and results in your retention policy. Removing it also removes deduplication protection and bindings for old calls. Back it up alongside the memory store.

The Recall client serializes requests. Bounded worker slots and timeouts keep slow operations from accumulating without limit. Defaults are one second for identity resolution and reads, three seconds for tool writes, and eight outstanding operations per adapter. Configure the client's transport timeout as well. aclose() waits for pending work before closing that client.

Development

cd python/recall-vapi
pip install -e '.[dev]' build
pytest tests/unit_tests -q
pytest tests/integration_tests -q  # real binaries from polign_db (no Vapi key)
python -m build

Tests cover Vapi payload variants, identity isolation, corrections across calls, duplicate delivery and restart, unknown outcomes, and HTTP credentials. Integration tests run the real Recall subprocess and an isolated Polign server. They do not exercise Vapi's live voice pipeline. The example README describes that final manual test.

The voice predicate JSON is copied from recall-livekit to avoid a dependency on a voice runtime. A test checks parity when both source packages are present. CI includes this package. Release tags use vapi/vX.Y.Z and must match the version in pyproject.toml. Publishing uses the PyPI trusted publisher for this repository's python-publish.yml workflow and pypi environment.

Release files for recall-vapi 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for recall-vapi 0.1.0
File Size Uploaded
recall_vapi-0.1.0.tar.gz 23.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for recall-vapi 0.1.0
File Interpreter ABI Platform
recall_vapi-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 42.6 kB

Release files / recall_vapi-0.1.0.tar.gz

Download URL recall_vapi-0.1.0.tar.gz
Size 23.7 kB
Tags Source
SHA-256 checksum
How to use checksums
c904fa4019775feeb03ca3fb45952c17655c25cbfb3f8fa3e23fef34763b3c72
BLAKE2b-256 checksum
How to use checksums
cd4d415a281fdd656ef9aae76929a8a25f0156945b51e6a0a5b03f7a84055977
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 22, 2026.

Transparency log

Release files / recall_vapi-0.1.0-py3-none-any.whl

Download URL recall_vapi-0.1.0-py3-none-any.whl
Size 19.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f53ee1528691dced443eb3acf3b907b921d2d9313e2a64a9bdf2b41778ef5b53
BLAKE2b-256 checksum
How to use checksums
ce80170ad9d69288a8d3df3d603322ccaf44445add05cecea68c960df7187743
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 22, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page