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
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,
    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

Recall is the only store. Call bindings and the tool ledger are records in a vapi_calls collection on the same Polign server as the memory, so every worker and host that shares the memory shares them, and one backup and retention policy covers both. RecallMemory.open records where that server is (the managed local database, or POLIGN_URL), and the adapter uses it unless you pass state=. Rename the collection with state=PolignState.for_memory(memory, collection="...").

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 is at-most-once execution for each (call_id, tool_call_id) while the records are retained, not atomic exactly-once delivery: Polign has no insert-if-absent write, so a reservation is a pending record written before the operation runs. A retry always finds it; only two deliveries of the same tool call within the same few milliseconds could both execute. A crash after a reservation may leave a pending operation, whether or not Recall committed it. Such operations are never automatically replayed; reconcile the record's fingerprint 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.

The records hold customer IDs and tool results and are kept until you delete them; include them in your retention policy. Deleting them also removes deduplication protection for old calls. If the state server is unreachable, the webhook answers 503 and Vapi retries. MemoryState keeps the same ledger in the adapter process for tests and one-off runs; any object with bind, subject, reserve, finish_tool, and finish_call methods can replace it.

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 across a worker 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.

Metadata

Release files for recall-vapi 0.2.1

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.2.1
File Size Uploaded
recall_vapi-0.2.1.tar.gz 26.1 kB Details

Built distribution (wheel)

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

Total release size: 46.7 kB

Release files / recall_vapi-0.2.1.tar.gz

Download URL recall_vapi-0.2.1.tar.gz
Size 26.1 kB
Tags Source
SHA-256 checksum
How to use checksums
6ae339dea3c8888c00a700b9dbb19d7af9c093fdb5265b7a54c3927721d4ff5f
BLAKE2b-256 checksum
How to use checksums
408cd66c8ff934deefecc11b2d46777adba02256f13e58225a35335cfc6b1d6e
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 Oct 13, 2026.

Transparency log

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

Download URL recall_vapi-0.2.1-py3-none-any.whl
Size 20.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
86392091c81386bf3b14ce51ab33a7f5ac0c0602fc6b1b4e60717003bfcda35b
BLAKE2b-256 checksum
How to use checksums
b72bd7a179bc2de2d37a5cad57808d6b622cfdca9b45775503b9ef191f5ef5a0
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 Oct 13, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.1 This release

2 release files

0.1.0

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