TurfAITurfAI User Guide
Modules

Event Bus / Triggers

How workflows start — webhooks, schedules, and event sources.

What it is

Triggers are the entry points that start a workflow. TurfAI has four: webhook, scheduled (cron), manual, and event sources via a unified Event Bus (push-based, starting with Google Drive folder watching and extensible to email/Slack). The Event Bus turns external events into workflow runs without polling, and writes every event to an audit log.

When to use it

  • Webhook — start from an external system's HTTP POST (JSON, form-data, or file upload). Best when the other system already knows when something happened (a form was submitted, a record changed).
  • Scheduled — recurring jobs (cron, timezone-aware). Best for time-driven work: nightly reports, weekly reminders, end-of-month reconciliation.
  • Event source — react to "a file landed in this Drive folder". Best when you own the folder but not the upload event — TurfAI watches Drive for you.
  • Manual — run on demand with custom JSON inputs. Best for testing and one-off backfills.

Picking a trigger

If an external app can call a URL, prefer a webhook — it is the most direct and gives you a polling token back. If nothing can call you but the data shows up on a clock, use a schedule. If files arrive in a Drive folder, use an event source so you don't have to instrument the uploader.

How it works

Process flow — event to execution:

Call flow — a Drive push trigger:

Sub-features

  • Webhook — secret-key auth (timing-safe), file uploads, auto-generated curl samples, polling token, event-type tagging, secret rotation with a grace window, optional origin pinning.
  • Scheduled — cron, timezone-aware, pause/resume (is_active), skip-if-running guard, next-run prediction.
  • Event sources (Pub/Sub) — Drive folder watch, subscriptions with filters, event logs, auto watch renewal, volume stats.
  • Manual — run with custom JSON inputs.

Configuration parameters

Webhook (api::webhook.webhook)

FieldTypeDefaultNotes
namestringRequired. Human-readable name.
secret_keystringautoRequired, private. 64-char hex; sent back as X-Webhook-Secret.
activityrelationThe workflow this webhook triggers.
activebooleantrueDisable instead of deleting to keep history.
event_typestringOptional tag (e.g. form_submission).
accept_filesbooleantrueAllow multipart/form-data uploads.
max_file_size_mbinteger20Per-file cap, range 1–50.
allowed_mime_typesjson (array)common doc typesWhitelist of MIME types for uploads.
auto_enable_ragbooleanfalseAuto-index uploaded files into the Knowledge Base.
allowed_originsjson (array)[]Empty = accept any origin; populated = request Origin/Referer must match (hostname or *.partner.com).
previous_secret_keystring (private)Old secret kept during the rotation grace window.
previous_secret_expires_atdatetime+24h on rotationGrace window end (WEBHOOK_ROTATION_GRACE_HOURS to override).
trigger_count / last_triggered_atinteger / datetimeRead-only usage stats.

Webhook default file size is 20 MB

The schema default for max_file_size_mb is 20 (range 1–50). Set it per webhook to match the largest file you expect; oversize uploads return 400 File upload failed.

Scheduled (api::workflow-schedule.workflow-schedule)

FieldTypeDefaultNotes
namestringRequired.
activityrelationRequired. Workflow to run.
cron_expressionstringRequired. Standard 5-field cron (see examples).
timezonestringUTCIANA name, e.g. America/New_York, Asia/Kolkata.
is_activebooleantrueThis is the pause switch — set false to pause.
skip_if_runningbooleantrueSkip a tick if the previous run is still going (prevents overlap).
inputsjsonDefault inputs passed to the workflow each run.
last_run_at / next_run_atdatetimeRead-only; next_run_at is the predicted next fire.
last_statusenumnever_runsuccess · failed · running · queued · never_run.
run_count / failure_countinteger0Read-only counters.

Event source (api::event-source.event-source)

FieldTypeDefaultNotes
namestringRequired.
source_typeenumRequired. google_drive · webhook · email · slack.
configjsonSource-specific configuration.
drive_item_idstringDrive folder/file ID (for google_drive).
drive_item_typeenumfile or folder.
drive_item_namestringDisplay name of the watched item.
watch_channel_idstringGoogle watch channel ID (set on files.watch()).
watch_resource_idstringGoogle watch resource ID.
watch_expirydatetimeWhen the Drive watch expires — auto-renewed before this.
page_tokenstringDrive changes-API token for incremental sync.
is_activebooleantruePause without deleting the watch record.
last_event_at / event_countdatetime / integerRead-only stats.
error_messagetextLast failure (e.g. expired/revoked watch).

Event subscription (api::event-subscription.event-subscription)

FieldTypeDefaultNotes
namestringHuman-readable name.
event_sourcerelationThe source to subscribe to.
activityrelationThe workflow to trigger when events match.
event_typesjson (array)["*"]e.g. ["file_created", "file_modified"] or ["*"] for all.
filterjsonOptional match criteria, e.g. { "mime_type": "application/pdf" }.
input_mappingjsonHow to map the event payload onto workflow inputs.
is_activebooleantrueToggle the subscription on/off.
trigger_count / last_triggered_atinteger / datetimeRead-only stats.

Dependencies

  • Event receiver endpoints in DMS (/api/event-bus/*, /api/webhooks/trigger/:id).
  • Google Drive Watch API (for Drive event sources) + OAuth, plus a verified HTTPS domain for push.
  • Job router / Redis + processors to execute the triggered workflow.
  • Knowledge Base / RAG when auto_enable_rag is on — uploaded files are indexed there.

Limitations

  • The unified Event Bus is an architecture/future-release design; Google Drive watching is the first source. Email and Slack sources are coming soon (the source_type enum already lists them).
  • Webhook file uploads are bounded by max_file_size_mb (1–50) and allowed_mime_types.
  • Google Drive watches expire (~24h) and are auto-renewed when expiry nears; a revoked OAuth grant stops renewal.
  • Webhook payload size and request rate are capped (see the developer webhook guide).

Tips & best practices

  • Choose the trigger by who knows about the event. External system knows → webhook. The clock knows → schedule. A Drive folder knows → event source.
  • Renewals are automatic, but watch watch_expiry. A scheduler renews Drive watches before they lapse. If error_message is set or last_event_at is stale, re-authorize the source.
  • Make workflows idempotent. Drive can deliver the same change more than once, and webhook callers may retry. Key your downstream writes on file_id / event_id so a duplicate trigger is a no-op.
  • Filter at the subscription, not in the workflow. A filter like { "mime_type": "application/pdf" } or narrowing event_types to ["file_created"] avoids spinning up runs you'll just discard.
  • Rotate webhook secrets without downtime. Rotation keeps previous_secret_key valid for the grace window so callers can deploy the new secret before the old one expires.
  • Pin origins for browser-triggered webhooks. Set allowed_origins to your domains to reject calls from anywhere else.
  • Pause, don't delete. Set is_active: false (schedule/source/subscription) or active: false (webhook) to keep counters and history.

Concrete examples

Cron syntax

Standard 5-field cron: minute hour day-of-month month day-of-week.

GoalCron
Every day at 9:00 AM0 9 * * *
Every Monday at 9:00 AM0 9 * * 1
Every 15 minutes*/15 * * * *
Top of every hour0 * * * *
9 AM on the 15th of each month0 9 15 * *
Weekdays at 6:30 PM30 18 * * 1-5
First day of the month, midnight0 0 1 * *

Always pair the cron with a timezone, e.g. Asia/Kolkata, so "9 AM" means 9 AM there — not UTC.

Webhook trigger payload + injected file variables

Trigger with a JSON body or multipart/form-data:

curl -X POST https://apisandbox.turfai.in/api/webhooks/trigger/1 \
  -H "X-Webhook-Secret: your_64_char_secret" \
  -F "resume=@candidate_resume.pdf" \
  -F "applicant_name=Jane Smith" \
  -F "email=jane@example.com"

Each uploaded file (field name resume) is injected into workflow inputs:

inputs.resume_document_id      // 789
inputs.resume_file_url         // "gs://turfdms/uploads/..."
inputs.applicant_name          // "Jane Smith"  (plain form fields pass through)
inputs._documents.resume       // { field_name, document_id, file_url, file_name, mime_type, file_size }
inputs._uploaded_files         // array of all uploaded files

Pattern: for a field named {field}, you get {field}_document_id and {field}_file_url, plus the structured _documents.{field} and the _uploaded_files array. The response also returns an execution_id and a short-lived polling_token.

Drive event source + subscription

# 1. Create the source (auto-registers a Drive watch)
curl -X POST https://apisandbox.turfai.in/api/event-sources \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{ "name": "Invoices Folder", "source_type": "google_drive",
        "drive_item_id": "folder_123", "drive_item_type": "folder" }'

# 2. Bind a workflow, filtered to new PDFs
curl -X POST https://apisandbox.turfai.in/api/event-subscriptions \
  -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
  -d '{ "name": "PDF invoices", "event_source": 5, "activity": 123,
        "event_types": ["file_created"],
        "filter": { "mime_type": "application/pdf" } }'

Injected event inputs (Drive)

When a Drive event fires, the bound workflow receives:

{
  trigger_type: "event",
  event_id: "evt_abc123",
  event_source: "google_drive",
  event_type: "file_created",
  file_id: "abc123",
  file_name: "invoice.pdf",
  file_mime_type: "application/pdf",
  folder_id: "folder_123"
}

Reference these in nodes as {{file_id}}, {{file_name}}, {{file_mime_type}}, {{folder_id}}, and {{trigger_type}}.

Troubleshooting

SymptomLikely causeFix
Webhook returns 401Missing/mismatched secretSend X-Webhook-Secret exactly; rotate and update the caller if unsure.
Webhook returns 403Webhook is active: falseRe-enable the webhook.
Webhook returns 400 on uploadFile exceeds max_file_size_mb or MIME not in allowed_mime_typesRaise the limit (≤50) or add the MIME type; confirm accept_files is true.
Webhook call rejected by originallowed_origins set and request origin not listedAdd the caller's hostname/*.subdomain or clear allowed_origins.
Scheduled job never runsis_active: false (paused)Set is_active: true.
Schedule "skips" a runPrevious run still going + skip_if_running: trueExpected; let it finish or set skip_if_running: false to allow overlap.
Schedule fires at the wrong timetimezone defaulting to UTCSet the IANA timezone; verify against next_run_at.
Drive events stop arrivingwatch_expiry passed / OAuth revokedCheck error_message; re-authorize the source so renewal resumes.
Drive file lands but no runSubscription filter / event_types don't matchLoosen filter (e.g. drop mime_type) or include the right event_type; check the event log for subscriptions_matched: 0.
Execution stuck in queuedJob router / Redis downVerify the job router and Redis are up.

How to test

Webhook

  1. POST /api/webhooks with an activity id; copy the returned url and secret_key.
  2. POST to that URL with X-Webhook-Secret and a JSON (or form-data) body — confirm a 200 with execution_id and polling_token.
  3. Poll GET /api/workflow-executions/:id with the polling token until status: completed.

Schedule

  1. Create a schedule with a near-future cron (e.g. */2 * * * *) and the correct timezone.
  2. Confirm next_run_at is populated, then wait for last_run_at to update and run_count to increment.
  3. Pause with is_active: false and confirm no further runs.

Drive event source + subscription

  1. Create a google_drive event source for a folder you own; confirm watch_channel_id / watch_expiry are set.
  2. Create a subscription binding a workflow, event_types: ["file_created"].
  3. Drop a file into the folder; confirm an event-log entry (subscriptions_matched: 1) and a triggered run with the injected file_id / file_name inputs.

APIs used

MethodPathPurpose
POST/api/webhooksCreate a webhook
POST/api/webhooks/trigger/:idTrigger a workflow (public, secret-auth)
POST/api/webhooks/:id/regenerate-secretRotate the webhook secret
POST/api/event-bus/drive-webhookDrive push receiver
POST/api/event-sourcesCreate event source (auto-registers watch)
POST/api/event-subscriptionsBind a workflow to a source
GET/api/event-logsView event history / audit trail
GET/api/workflow-executions/:idPoll execution status

Roadmap: Google Drive event sources are live today. Email and Slack event sources are coming soon — the same subscription + filter model will apply.

On this page