Domain: fraya

Fraya API Surface

Agent-oriented map of the HTTP endpoints currently exposed by this application.

Use this page as the fastest way to answer:

  • what can this app already do over HTTP;
  • which endpoint to call for a given task;
  • what data shape to expect back;
  • which endpoints are intended as stable integration points vs internal helpers.

Decision guide

If you need to:

  • inspect or render a prompt from the repository → use /api/prompts/:name
  • get the current curated Synthesia template registry for video generation → use /api/video/templates
  • send an arbitrary system/user prompt bundle to the Dify runtime → use /api/run

Stable integration endpoints

These endpoints are intended to be used by agents, workflows, or external tooling.

GET /api/prompts/:name

Returns prompt metadata and the stored prompt body from prompts/.

Use when:

  • you need to discover which variables a prompt expects;
  • you need prompt metadata before building a request;
  • you need to inspect the current stored prompt definition.

Response shape:

{
  "name": "tool_synthesia_choose_template",
  "type": "dynamic",
  "version": 3,
  "variables": [
    { "name": "available_templates", "description": "..." }
  ],
  "example_request": {
    "available_templates": "Example content"
  },
  "output": {
    "format": "json",
    "schema": "{ ... }"
  },
  "components": [],
  "system": "You are ...",
  "user": "- Course title: {{ course_title }}"
}

Notes:

  • The external API always returns system and user.
  • If a prompt file is authored as a single-message prompt, the API normalizes it to: system: "" and user: "<prompt text>".
  • example_request is a generated mock payload shaped from the declared variables. Use it as a starting point for POST bodies, especially for nested objects and arrays.
  • This endpoint reads prompt YAML from the repository. It does not execute a model.

POST /api/prompts/:name

Renders a stored prompt with runtime variables substituted.

Use when:

  • a workflow needs the current prompt text from the repo;
  • you want Dify or another orchestrator to consume prompt instructions over HTTP instead of copying them manually;
  • you need injection content already resolved into the rendered result.

Request body:

{
  "course": {
    "title": "Le 7 abitudini per essere più efficace"
  },
  "section": {
    "title": "La vittoria pubblica: dall'io al noi"
  }
}

Response shape:

{
  "system": "You are ...",
  "user": "- Course title: Le 7 abitudini per essere più efficace\n- Video title: La vittoria pubblica: dall'io al noi"
}

Error behavior:

  • 404 Not Found if the prompt name does not exist:
{
  "code": "PROMPT_NOT_FOUND",
  "message": "Prompt \"tool_missing\" not found"
}
  • 400 Bad Request if the request body is not a valid JSON object:
{
  "code": "INVALID_JSON_BODY",
  "message": "Request body must be a valid JSON object."
}
  • 422 Unprocessable Entity if required template expressions remain unresolved after rendering:
{
  "code": "UNRESOLVED_TEMPLATE_EXPRESSIONS",
  "message": "Prompt render failed because required variables are missing or unresolved.",
  "unresolved": ["template_id", "template_title"],
  "missing_variables": ["template_id", "template_title"],
  "rendered_partial": {
    "system": "You are ... {{ template_id }} ...",
    "user": "Template: {{ template_title }}"
  }
}

Notes:

  • The renderer supports nested paths ({{ course.title }}), conditionals, loops, basic comparisons, or / and, and |length.
  • POST bodies may use nested JSON objects and arrays; flat dotted keys remain accepted for compatibility.
  • Missing variables now cause POST to fail with 422 if they leave unresolved template expressions, for example {{ section.title }} or {{ template_id }}.
  • missing_variables contains unresolved expressions that look like variable paths and are absent from the input body.
  • This endpoint is both a renderer and a final prompt-readiness validator.
  • It is the correct endpoint to use when a workflow wants prompt logic from the repo.
  • The rendered response always uses the pair system + user.

Reusable Dify helper:

  • A reusable workflow wrapper around this endpoint is stored in workflows/fraya/render-prompt/render-prompt.raw.yml.
  • Use that workflow when Dify needs prompt loading + input validation + normalized system_prompt / user_prompt outputs as a reusable subflow.

GET /api/video/templates

Builds the current video template registry for the Synthesia workflow.

Use when:

  • a workflow needs the curated list of supported Synthesia templates;
  • you want live Synthesia metadata without using Google Sheets;
  • you need a repo-controlled allowlist combined with current workspace template data.

What it does:

  1. Fetches the full template catalog from Synthesia using server-side credentials.
  2. Filters it by Fraya's curated allowlist of supported template IDs.
  3. Splits the result into presentations and talking_head.
  4. Returns the filtered live metadata.

Response shape:

{
  "registry_source": "repo_allowlist_plus_synthesia",
  "allowed_template_ids_count": 34,
  "workspace_templates_count": 112,
  "selected_templates_count": 34,
  "presentations": [
    {
      "template_id": "0c471c30-f3fe-432b-9fae-642b096238c1",
      "template_title": "A-F",
      "template_description": "Single principle or idea — no enumeration",
      "template_variables": [
        { "label": "slide_1_script", "type": "string" }
      ]
    }
  ],
  "talking_head": [
    {
      "template_id": "9c23e3c3-401b-4373-9ca2-e15b669974a3",
      "template_title": "[de] Talking head (A-Q-A-F)",
      "template_description": "...",
      "template_variables": [
        { "label": "slide_1_script", "type": "string" }
      ]
    }
  ],
  "legacy_language_filtering": false,
  "requested_language": "Default (auto detection)",
  "requested_format": "VideoTalkingHead",
  "note": "language and format are currently informational only; filtering is based on the repo allowlist and runtime consumers can still select the needed subset."
}

Optional query params:

  • language
  • format

Current behavior:

  • accepted for compatibility and debugging;
  • currently informational only;
  • filtering is driven by the repo allowlist, not by language-specific registry columns.

Important:

  • This endpoint is the intended replacement for the old Google Sheets-based template allowlist.
  • Synthesia remains the source of live metadata (title, description, variables).
  • The repository remains the source of truth for which template IDs are allowed.

Internal helper endpoint

This endpoint exists and works, but should be treated as a thinner runtime helper, not the primary repo-integration surface.

POST /api/run

Proxies a raw { model, system, user } request to the configured Dify workflow runtime.

Use when:

  • you want to quickly execute a prompt bundle against the Dify backend from the docs app;
  • you need an internal testing surface for prompt execution.

Request body:

{
  "model": "gemini-2.5-pro",
  "system": "You are ...",
  "user": "..."
}

Notes:

  • This endpoint depends on DIFY_API_URL and DIFY_API_KEY.
  • It is less repository-centric than /api/prompts/:name.
  • Prefer /api/prompts/:name when the task is about loading prompt logic from the repo.

Environment variables

Current endpoints depend on these server-side variables:

VariableUsed byPurpose
DIFY_API_URL/api/runTarget Dify workflow execution endpoint
DIFY_API_KEY/api/runAuth for the Dify runtime
SYNTHESIA_API_BASE_URL/api/video/templatesBase URL for Synthesia API
SYNTHESIA_API_KEY/api/video/templatesAuth for server-side Synthesia template fetch

Recommended usage patterns

  • For workflow prompt loading: call /api/prompts/:name
  • For video template discovery: call /api/video/templates
  • For ad hoc execution from the docs app: call /api/run

The intended long-term pattern is:

repo prompts + repo-controlled template registry + workflow orchestration in Dify