Skip to main content

tidefold-client

A small Python client for starting workflow runs on a tidefold deployment, waiting for them, and reading what they produced.

pip install "tidefold-client==<your platform version>"

Install the version that matches your deployment's platform release. The client warns when it talks to a server on a different release. There is no compatibility promise across versions.

Quick start

import asyncio

from tidefold_client import AsyncClient


async def main() -> None:
    async with AsyncClient.from_env() as client:  # TIDEFOLD_API_URL, TIDEFOLD_API_TOKEN
        run = await client.workflows.start(
            "invoice_review",
            inputs={"reference": "INV-1042"},
            files={"document": "invoice.pdf"},
            subject="INV-1042",
        )
        await run.wait(timeout=1800)
        print((await run.results())["marked_results"])


asyncio.run(main())

TIDEFOLD_API_URL is the API root: https://<your host>/api on a deployment, http://localhost:8000 on a local stack. There is no default.

The API token

An administrator creates tokens under Settings → API tokens. A token is shown once; keep it in a secret store and pass it through the environment. Grant only the scopes your script needs:

Scope Needed for
workflow_read workflows.list(), workflows.get_id(), workflows.start() by name
run_create workflows.start()
run_file_write files.upload(), files.upload_bytes(), workflows.start() with files
run_read runs.get(), runs.wait(), runs.results(), runs.usage(), runs.list(), files.download(), files.download_bytes()
run_cancel runs.cancel()
inbox_read runs.requests()

"Own data only" visibility suits scripts: the token then sees only the runs it started. A local stack running without auth takes no token.

The subclients

The client has one subclient per resource, all sharing one session. One client may be shared by concurrent tasks. async with closes it, and so does await client.aclose(). http= takes your own httpx.AsyncClient, which the client never closes.

client.workflows

  • start(name, *, inputs, files, file_ids, subject, correlation_id) uploads each file in files and starts a run on the workflow's latest published release (channel="draft" runs the current draft). It returns a Run at once. workflow_id= starts by id instead of name.
  • file_ids takes files uploaded earlier, by id, in the same shape as files: one id or a list per input. A start with file_ids only uploads nothing, so uploading and starting can be separate steps. An input goes in files or file_ids, not both.
  • list() returns the workflows the token can see; get_id(name) resolves a name to its stable id.

client.runs

These take a run id:

  • get() reads the run as it stands.
  • wait() polls until the run ends. It returns the run on COMPLETED and raises RunFailed, RunCancelled or WaitTimeout. A server restarting during a deploy is waited out. Cancelling the waiting task, like a timeout, leaves the run going on the server.
  • results() returns results per step and marked_results, the outputs the workflow declares. usage(), requests() and cancel() do what their names say.

list(workflow_id=None, *, correlation_id, correlation_tags, status) finds runs again without their ids. It yields runs newest first and follows the pages for you; stop iterating to stop.

  • correlation_id matches the id given at start() exactly.
  • correlation_tags matches the parts of that id between &s, each one whole and in any position. Every tag given must match, so case_id=4711&doc=12 is found by ["case_id=4711"].
  • A correlation_id takes at most four tags, none empty, and 512 characters. A & inside a value must be percent-encoded.
async for run in client.runs.list(correlation_tags=["case_id=4711"]):
    print(run["id"], run["status"], run["correlation_id"])

client.files

  • upload_bytes(name, content, *, mime_type=None) declares the file, uploads it to the signed URL (never with the token) and finalizes it; it returns the file's record, whose id goes into file_ids. Without mime_type, the type is guessed from name.
  • upload(path) does the same for a file on disk.
  • download(file_id, path) streams a file, such as a produced document, to disk; download_bytes(file_id) returns its content.

A Run is client.runs with the id filled in: await run.wait(), await run.results() and so on. client.run(run_id) gives one for a run started earlier.

Starting is not idempotent. The client never retries start(), and calling it twice starts two runs, even with the same correlation_id.

Receiving webhooks

A workflow's Notifications tab can POST run events to your endpoint, signed per Standard Webhooks. webhooks.verify(body, headers, secret) checks the signature and the timestamp and returns the event: id, type, version, occurred_at and data.

  • body is the raw request body, exactly as received. Never decode or re-serialise it first.
  • secret is the subscription's whsec_… signing secret. During a rotation, pass a list of the old and the new one.
  • tolerance=300 is how many seconds the timestamp may be off.

A minimal receiver, here with FastAPI:

import os

from fastapi import BackgroundTasks, FastAPI, Request, Response

from tidefold_client import AsyncClient, webhooks

WEBHOOK_SECRET = os.environ["TIDEFOLD_WEBHOOK_SECRET"]
app = FastAPI()
seen: set[str] = set()  # use your database: deliveries repeat across restarts


async def handle(event: webhooks.WebhookEvent) -> None:
    async with AsyncClient.from_env() as client:
        run = await client.runs.get(event.data["run_id"])
        ...


@app.post("/hooks/tidefold")
async def receive(request: Request, tasks: BackgroundTasks) -> Response:
    try:
        event = webhooks.verify(
            await request.body(), request.headers, WEBHOOK_SECRET
        )
    except webhooks.WebhookVerificationError:
        return Response(status_code=400)
    except webhooks.WebhookVersionError:
        return Response(status_code=503)  # retried for 72 h: upgrade the client
    if event.id not in seen:
        seen.add(event.id)
        tasks.add_task(handle, event)
    return Response(status_code=204)

Answer fast: anything that takes longer than 10 s is retried. The same event can arrive more than once, so dedupe on event.id. Match on event.type and ignore types you do not handle, because new ones can appear in any release. The body carries no run content; fetch it with the client. Webhooks do not replace polling: a receiver that must not miss a run end still checks runs.get() for the runs it is waiting on.

Errors

Every exception derives from TidefoldError.

Exception Meaning
AuthError 401: token missing, wrong, expired or revoked
PermissionDenied 403: the token lacks the scope named in the message
NotFound 404: no such run or file; for a workflow, also a category the token cannot see
Conflict 409: workflow never published, or results read while the run is still running
InvalidRequest 400, 413, 422: a missing input, an oversized or unsupported file
UploadError a file did not upload; no run was started
RunFailed, RunCancelled, WaitTimeout raised by wait()
NetworkError the server could not be reached after retries
UnsupportedByServer the server's release predates the call, such as list() with correlation filters
WebhookVerificationError webhooks.verify(): not an authentic event; do not trust it
WebhookVersionError webhooks.verify(): authentic, but a body version this client does not know

License

Apache-2.0.

Metadata

Release files for tidefold-client 0.0.229

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

Source distribution (sdist)

Source distribution for tidefold-client 0.0.229
File Size Uploaded
tidefold_client-0.0.229.tar.gz 19.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for tidefold-client 0.0.229
File Interpreter ABI Platform
tidefold_client-0.0.229-py3-none-any.whl Python 3 none any Details

Total release size: 43.5 kB

Release files / tidefold_client-0.0.229.tar.gz

Download URL tidefold_client-0.0.229.tar.gz
Size 19.5 kB
Tags Source
SHA-256 checksum
How to use checksums
cc5ff1bc37b50424c8b86c12dc1ffe989348adf5518684dfcbbf33862f156551
BLAKE2b-256 checksum
How to use checksums
d5db1d8d9d7eb5a60edfef81550d4dee87204c16b5615f939324d57cd588ecc8
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 12, 2026.

Transparency log

Release files / tidefold_client-0.0.229-py3-none-any.whl

Download URL tidefold_client-0.0.229-py3-none-any.whl
Size 24.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0636881f4b99a1cf27847c306e5ffd51be80c9ecb23b4f6fb370959fd9dd16a2
BLAKE2b-256 checksum
How to use checksums
212996c9757f0a2e1628e340812c1926bfd2a1eebf15fc5a1cff8647b892d4e9
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 12, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.0.229 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