Skip to content
FolioSign inStart your trial

Docs · Start here

Quickstart

Create a key, add a teaching, wait for it to process, read the transcript and note, ask a question and receive a webhook.

This walks one recording from upload to a cited answer. It takes about ten minutes, most of it waiting for processing.

1. Create a key#

In the Folio console open Developers, then API keys, and create a key. Choose the scopes you need. For this walk-through select Full access, or pick media:upload, teachings:read, notes:read, ask:create, ask:read and webhooks:write. Creating a key asks you to sign in again. The secret is shown once; copy it now.

Code
export FOLIO_API_URL="https://your-api-host.example"
export FOLIO_API_KEY="folio_live_xxxxxxxxxxxxxxxxxxxxxxxx"

Check the key works by reading the church's plan and usage:

curl -X GET "$FOLIO_API_URL/v1/billing" \
  -H "Authorization: Bearer $FOLIO_API_KEY"
Response
{
  "status": true,
  "message": "billing",
  "data": { "object": "billing", "standing": { "state": "active" }, "cycle": { "start": "2026-11-01T00:00:00Z", "end": "2026-12-01T00:00:00Z" } }
}

A 401 means the key is wrong or expired. A 403 means the key lacks billing:read; try another call or add the scope.

2. Add a teaching#

Creating a teaching returns a signed upload URL. Send an Idempotency-Key so a retry does not create a second teaching.

curl -X POST "$FOLIO_API_URL/v1/teachings" \
  -H "Authorization: Bearer $FOLIO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: sunday-2026-11-01-create" \
  -d '{
  "title": "Sunday, 1 November: Nothing can separate us",
  "filename": "sunday.mp3",
  "content_type": "audio/mpeg"
}'
Response
{
  "status": true,
  "message": "upload ready",
  "data": {
    "object": "upload",
    "teaching": { "id": "tch_0192f7a8-3b1c-7d2e-9a40-5c6d7e8f9012", "object": "teaching", "name": "Sunday, 1 November: Nothing can separate us" },
    "key": "originals/0192f7a8-3b1c-7d2e-9a40-5c6d7e8f9099/0192f7a8-3b1c-7d2e-9a40-5c6d7e8f9012/sunday.mp3",
    "url": "https://files.example.org/originals/0192f7a8-3b1c-7d2e-9a40-5c6d7e8f9099/0192f7a8-3b1c-7d2e-9a40-5c6d7e8f9012/sunday.mp3?X-Signature=abc123",
    "expires_at": "2026-11-01T10:15:00Z"
  }
}

Upload the recording to data.url with a plain PUT and the same content type. No Folio key goes on this request; the URL is signed.

curl -X PUT "$UPLOAD_URL" -H "Content-Type: audio/mpeg" --data-binary @sunday.mp3

Then tell Folio the file is there, using the key from the first response:

curl -X POST "$FOLIO_API_URL/v1/teachings/tch_0192f7a8-3b1c-7d2e-9a40-5c6d7e8f9012/upload" \
  -H "Authorization: Bearer $FOLIO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: sunday-2026-11-01-complete" \
  -d '{
  "key": "originals/0192f7a8-3b1c-7d2e-9a40-5c6d7e8f9099/0192f7a8-3b1c-7d2e-9a40-5c6d7e8f9012/sunday.mp3"
}'

3. Wait for processing#

Processing is asynchronous. Poll the teaching until status is ready or failed. Every few seconds is fine; a long recording takes a few minutes.

curl -X GET "$FOLIO_API_URL/v1/teachings/tch_0192f7a8-3b1c-7d2e-9a40-5c6d7e8f9012" \
  -H "Authorization: Bearer $FOLIO_API_KEY"
Response
{
  "status": true,
  "message": "teaching",
  "data": { "id": "tch_0192f7a8-3b1c-7d2e-9a40-5c6d7e8f9012", "object": "teaching", "status": "processing", "note_status": "none", "duration_seconds": 2412 }
}

The statuses are awaiting_upload, queued, processing, ready and failed. When it is failed, failure_reason says why in plain words. Rather than polling you can subscribe to teaching.ready (step 6).

4. Read the transcript#

curl -X GET "$FOLIO_API_URL/v1/teachings/tch_0192f7a8-3b1c-7d2e-9a40-5c6d7e8f9012/transcript" \
  -H "Authorization: Bearer $FOLIO_API_KEY"
Response
{
  "status": true,
  "message": "segments",
  "data": [
    { "object": "segment", "start_ms": 0, "end_ms": 6400, "speaker": "Speaker 1", "text": "Good morning, church. Open your Bibles to Romans chapter eight." }
  ]
}

Asking for the transcript before the teaching is ready answers 404 with a message that says so.

5. Read the note#

Folio writes a note from the transcript, then a person (or the church's automatic approval) reviews it. Until it is published, the call answers 404. That is expected. It is the review step working.

curl -X GET "$FOLIO_API_URL/v1/teachings/tch_0192f7a8-3b1c-7d2e-9a40-5c6d7e8f9012/note" \
  -H "Authorization: Bearer $FOLIO_API_KEY"
Response
{
  "status": true,
  "message": "note",
  "data": {
    "id": "note_0192f7a8-3b1c-7d2e-9a40-5c6d7e8f9012",
    "object": "note",
    "status": "published",
    "summary": "Nothing in all creation can separate us from the love of God in Christ.",
    "sections": [{ "h": "Who can condemn?", "body": "Paul answers with Christ's death, resurrection and intercession." }]
  }
}

6. Ask a question#

Answers are written from the church's approved teaching and cite their sources. The answer is asynchronous: the first response holds a pending answer.

curl -X POST "$FOLIO_API_URL/v1/conversations" \
  -H "Authorization: Bearer $FOLIO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: faq-forgiveness-1" \
  -d '{
  "title": "Forgiveness",
  "input": "What did we teach about forgiveness?"
}'

Poll the conversation until the last message's kind is no longer pending:

curl -X GET "$FOLIO_API_URL/v1/conversations/conv_0192f7a8-3b1c-7d2e-9a40-5c6d7e8f9012" \
  -H "Authorization: Bearer $FOLIO_API_KEY"
Response
{
  "status": true,
  "message": "conversation",
  "data": {
    "id": "conv_0192f7a8-3b1c-7d2e-9a40-5c6d7e8f9012",
    "messages": [
      { "role": "user", "kind": "question", "text": "What did we teach about forgiveness?" },
      { "role": "assistant", "kind": "answer", "short_answer": "Forgiveness is a decision to release a debt, as God released ours. [1]", "sources": [{ "number": 1, "title": "Seventy times seven", "speaker": "Pastor Ade" }] }
    ]
  }
}

If the church has not taught on the subject the answer has kind not_covered. That is the honest answer, not an error.

7. Hear about it by webhook#

Register an https endpoint and subscribe it to the events you care about. The signing secret is returned once.

curl -X POST "$FOLIO_API_URL/v1/webhook-endpoints" \
  -H "Authorization: Bearer $FOLIO_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: endpoint-sunday-pipeline" \
  -d '{
  "name": "Sunday pipeline",
  "url": "https://hooks.example.org/folio",
  "event_types": [
    "teaching.ready",
    "note.published"
  ],
  "mode": "live"
}'

Check your receiver with a signed test event, then read webhooks to verify signatures in your own code:

curl -X POST "$FOLIO_API_URL/v1/webhook-endpoints/whe_0192f7a8-3b1c-7d2e-9a40-5c6d7e8f9012/test" \
  -H "Authorization: Bearer $FOLIO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "event_type": "note.published"
}'

Where next#

Verified against API version 0.0.0-dev. Generated from the live OpenAPI document.