Complete reference for all Sayify.pro API endpoints with examples and response payloads.

📡 API Endpoints Reference

Base URL: https://sayify.pro/api/v1/

All endpoints require Bearer token authentication. See Authentication.


POST /v1/links/ — Create Feedback Link

Create a new response collection link with custom questions. Returns 201 on success. All 11 question types are supported inline.

Request Body:

Field Type Required Description
name string Display name for the feedback link.
slug string URL slug — auto-generated from name if omitted. Must be unique per workspace.
status string "active" (default) or "paused".
settings.description string Shown on the public intake page header.
settings.thankYouMessage string Shown after submission (default: "Thanks for your response!").
settings.redirectUrl string Redirect respondent to this URL after submission.
settings.tts_enabled boolean Read questions aloud using text-to-speech (default: false).
settings.live_transcription boolean Show live transcription while recording voice (default: false).
questions array Array of question objects (see question fields below).
on_finished.webhook_url string Auto-create a webhook that fires on response submission.
on_finished.webhook_secret string Secret for webhook signature verification.

Question Fields:

Field Type Required Description
prompt string The question text shown to the respondent.
type string Response type (default: "voice"). See Question Types.
field_key string snake_case identifier in structured output — auto-generated from prompt if omitted.
order integer Display order (0-indexed, default: 0).
required boolean Whether the question must be answered (default: false).
configuration object Type-specific settings (see Question Types).
condition object Conditional visibility: { field, operator, value }. See Conditional Logic.
enable_dynamic_probing boolean AI follow-up if answer is unclear (default: false).
fallback_to_text boolean Allow text fallback for voice questions (default: true).
evaluator_config object { clarity_threshold, max_follow_ups, custom_fields } — used when probing is on.

Example (cURL):

curl -X POST https://sayify.pro/api/v1/links/ \
  -H "Authorization: Bearer sk_live_AbCdEfGhIjKlMnOpQrStUvWxYz0123456789xyz" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Product Feedback Survey",
    "slug": "product-feedback-survey",
    "status": "active",
    "settings": {
      "description": "Tell us about your experience with our product.",
      "thankYouMessage": "Thanks for your feedback!",
      "redirectUrl": "https://example.com/thank-you"
    },
    "questions": [
      {
        "prompt": "Tell us about your experience",
        "type": "voice",
        "field_key": "experience",
        "order": 0,
        "required": true,
        "configuration": { "max_duration": 120, "enable_ai_evaluation": true },
        "enable_dynamic_probing": true,
        "evaluator_config": { "clarity_threshold": 60, "max_follow_ups": 2, "custom_fields": ["Summary"] }
      },
      {
        "prompt": "How likely are you to recommend us? (1-10)",
        "type": "number",
        "field_key": "nps_score",
        "order": 1,
        "required": true,
        "configuration": { "min": 1, "max": 10, "step": 1 }
      },
      {
        "prompt": "What is your email address?",
        "type": "email",
        "field_key": "email_address",
        "order": 2,
        "required": true
      }
    ],
    "on_finished": {
      "webhook_url": "https://example.com/webhook",
      "webhook_secret": "my-webhook-secret"
    }
  }'

Example (JavaScript):

const payload = {
  name: "Product Feedback Survey",
  slug: "product-feedback-survey",
  status: "active",
  settings: {
    description: "Tell us about your experience with our product.",
    thankYouMessage: "Thanks for your feedback!",
    redirectUrl: "https://example.com/thank-you"
  },
  questions: [
    {
      prompt: "Tell us about your experience",
      type: "voice", field_key: "experience", order: 0, required: true,
      configuration: { max_duration: 120, enable_ai_evaluation: true },
      enable_dynamic_probing: true,
      evaluator_config: { clarity_threshold: 60, max_follow_ups: 2, custom_fields: ["Summary"] }
    },
    {
      prompt: "How likely are you to recommend us? (1-10)",
      type: "number", field_key: "nps_score", order: 1, required: true,
      configuration: { min: 1, max: 10, step: 1 }
    },
    {
      prompt: "What is your email address?",
      type: "email", field_key: "email_address", order: 2, required: true
    }
  ],
  on_finished: {
    webhook_url: "https://example.com/webhook",
    webhook_secret: "my-webhook-secret"
  }
};

const response = await fetch("https://sayify.pro/api/v1/links/", {
  method: "POST",
  headers: {
    "Authorization": "Bearer sk_live_AbCdEfGhIjKlMnOpQrStUvWxYz0123456789xyz",
    "Content-Type": "application/json"
  },
  body: JSON.stringify(payload)
});

const link = await response.json();
console.log(link.uuid, link.public_url); // 201 Created

Example (Python):

import requests

payload = {
    "name": "Product Feedback Survey",
    "slug": "product-feedback-survey",
    "status": "active",
    "settings": {
        "description": "Tell us about your experience with our product.",
        "thankYouMessage": "Thanks for your feedback!",
        "redirectUrl": "https://example.com/thank-you"
    },
    "questions": [
        {
            "prompt": "Tell us about your experience",
            "type": "voice", "field_key": "experience", "order": 0, "required": True,
            "configuration": {"max_duration": 120, "enable_ai_evaluation": True},
            "enable_dynamic_probing": True,
            "evaluator_config": {"clarity_threshold": 60, "max_follow_ups": 2, "custom_fields": ["Summary"]}
        },
        {
            "prompt": "How likely are you to recommend us? (1-10)",
            "type": "number", "field_key": "nps_score", "order": 1, "required": True,
            "configuration": {"min": 1, "max": 10, "step": 1}
        },
        {
            "prompt": "What is your email address?",
            "type": "email", "field_key": "email_address", "order": 2, "required": True
        }
    ],
    "on_finished": {
        "webhook_url": "https://example.com/webhook",
        "webhook_secret": "my-webhook-secret"
    }
}

response = requests.post(
    "https://sayify.pro/api/v1/links/",
    headers={
        "Authorization": "Bearer sk_live_AbCdEfGhIjKlMnOpQrStUvWxYz0123456789xyz",
        "Content-Type": "application/json"
    },
    json=payload
)

link = response.json()
print(link["uuid"], link["public_url"])  # 201 Created

Response (201 Created):

{
  "uuid": "ea92fd15-7563-447f-a75d-0d61570f4c73",
  "name": "Product Feedback Survey",
  "slug": "product-feedback-survey",
  "status": "active",
  "settings": {
    "description": "Tell us about your experience with our product.",
    "thankYouMessage": "Thanks for your feedback!",
    "redirectUrl": "https://example.com/thank-you"
  },
  "questions": [
    {
      "uuid": "4041f961-328f-4c6d-a5d3-2125a80f1ec5",
      "prompt": "Tell us about your experience",
      "field_key": "experience",
      "response_type": "voice",
      "configuration": { "max_duration": 120, "enable_ai_evaluation": true },
      "order": 0,
      "required": true,
      "condition": {},
      "enable_dynamic_probing": true,
      "fallback_to_text": true,
      "evaluator_config": {
        "clarity_threshold": 60,
        "max_follow_ups": 2,
        "custom_fields": ["Summary"]
      },
      "date_created": "2026-02-22T17:15:55.291121Z",
      "date_updated": "2026-02-22T17:15:55.291131Z"
    },
    {
      "uuid": "f4369f61-cdf8-423d-a9d0-5bf74c61761b",
      "prompt": "How likely are you to recommend us? (1–10)",
      "field_key": "nps_score",
      "response_type": "number",
      "configuration": { "min": 1, "max": 10, "step": 1 },
      "order": 1,
      "required": true,
      "condition": {},
      "enable_dynamic_probing": false,
      "fallback_to_text": true,
      "evaluator_config": {},
      "date_created": "2026-02-22T17:15:55.295912Z",
      "date_updated": "2026-02-22T17:15:55.295920Z"
    },
    {
      "uuid": "1eb6072c-0f56-457c-8c12-8a74f51edef7",
      "prompt": "What is your email address?",
      "field_key": "email_address",
      "response_type": "email",
      "configuration": {},
      "order": 2,
      "required": true,
      "condition": {},
      "enable_dynamic_probing": false,
      "fallback_to_text": true,
      "evaluator_config": {},
      "date_created": "2026-02-22T17:15:55.297838Z",
      "date_updated": "2026-02-22T17:15:55.297848Z"
    }
  ],
  "response_count": 0,
  "public_url": "https://acme-corp.sayify.pro/i/product-feedback-survey",
  "date_created": "2026-02-22T17:15:55.289912Z",
  "date_updated": "2026-02-22T17:15:55.289922Z"
}

Possible Errors:

Status Code Description
400 NAME_REQUIRED The name field is missing from the request body.
400 SLUG_CONFLICT The provided slug already exists — omit it to auto-generate.
403 LINK_LIMIT_REACHED Workspace has reached its plan link quota.
403 INSUFFICIENT_SCOPE API key missing scope: links:write.

Retrieve all feedback links in your workspace.

cURL:

curl https://sayify.pro/api/v1/links/ \
  -H "Authorization: Bearer sk_live_AbCdEfGhIjKlMnOpQrStUvWxYz0123456789xyz"

JavaScript:

const response = await fetch("https://sayify.pro/api/v1/links/", {
  headers: { "Authorization": "Bearer sk_live_AbCdEfGhIjKlMnOpQrStUvWxYz0123456789xyz" }
});
const links = await response.json();
links.forEach(link => console.log(link.name, link.public_url));

Python:

import requests

response = requests.get(
    "https://sayify.pro/api/v1/links/",
    headers={"Authorization": "Bearer sk_live_AbCdEfGhIjKlMnOpQrStUvWxYz0123456789xyz"}
)
links = response.json()
for link in links:
    print(link["name"], link["public_url"])

Response (200 OK):

[
  {
    "uuid": "f87ff124-d1ca-4dd2-b440-b9e719931a2b",
    "name": "Product Feedback Survey",
    "slug": "product-feedback-survey",
    "status": "active",
    "settings": {
      "description": "Tell us about your experience with our product.",
      "redirectUrl": "https://example.com/thank-you",
      "thankYouMessage": "Thanks for your feedback!"
    },
    "questions": [ "..." ],
    "response_count": 30,
    "public_url": "https://acme-corp.sayify.pro/i/product-feedback-survey",
    "date_created": "2026-02-17T07:39:11.883452Z",
    "date_updated": "2026-02-19T17:06:15.899034Z"
  }
]

Retrieve a single feedback link with all its questions.

Parameter Type Required Description
link_uuid uuid UUID of the link (from the create response).

cURL:

curl https://sayify.pro/api/v1/links/f87ff124-d1ca-4dd2-b440-b9e719931a2b/ \
  -H "Authorization: Bearer sk_live_AbCdEfGhIjKlMnOpQrStUvWxYz0123456789xyz"

JavaScript:

const linkUuid = "f87ff124-d1ca-4dd2-b440-b9e719931a2b";

const response = await fetch(`https://sayify.pro/api/v1/links/${linkUuid}/`, {
  headers: { "Authorization": "Bearer sk_live_AbCdEfGhIjKlMnOpQrStUvWxYz0123456789xyz" }
});

const link = await response.json();
console.log(`${link.name} — ${link.questions.length} questions, ${link.response_count} responses`);

Python:

import requests

link_uuid = "f87ff124-d1ca-4dd2-b440-b9e719931a2b"
response = requests.get(
    f"https://sayify.pro/api/v1/links/{link_uuid}/",
    headers={"Authorization": "Bearer sk_live_AbCdEfGhIjKlMnOpQrStUvWxYz0123456789xyz"}
)

link = response.json()
print(f"{link['name']} — {len(link['questions'])} questions, {link['response_count']} responses")

Returns the full link object with all questions, settings, and response count (same shape as the create response).

Possible Errors:

Status Code Description
403 INSUFFICIENT_SCOPE API key missing scope: links:read.
404 No link with that UUID exists in this workspace.

Permanently delete a feedback link and all its questions. This cannot be undone. Response data collected through this link is retained.

Parameter Type Required Description
link_uuid uuid UUID of the link to delete.

cURL:

curl -X DELETE https://sayify.pro/api/v1/links/0b4d51a8-0a69-48bf-8113-fc9940efa9c0/ \
  -H "Authorization: Bearer sk_live_AbCdEfGhIjKlMnOpQrStUvWxYz0123456789xyz"

JavaScript:

const linkUuid = "0b4d51a8-0a69-48bf-8113-fc9940efa9c0";

const response = await fetch(`https://sayify.pro/api/v1/links/${linkUuid}/`, {
  method: "DELETE",
  headers: { "Authorization": "Bearer sk_live_AbCdEfGhIjKlMnOpQrStUvWxYz0123456789xyz" }
});

const result = await response.json();
console.log(result.message); // "Feedback link deleted successfully"

Python:

import requests

link_uuid = "0b4d51a8-0a69-48bf-8113-fc9940efa9c0"
response = requests.delete(
    f"https://sayify.pro/api/v1/links/{link_uuid}/",
    headers={"Authorization": "Bearer sk_live_AbCdEfGhIjKlMnOpQrStUvWxYz0123456789xyz"}
)

result = response.json()
print(result["message"])  # "Feedback link deleted successfully"

Response (200 OK):

{
  "message": "Feedback link deleted successfully"
}

Possible Errors:

Status Code Description
403 INSUFFICIENT_SCOPE API key missing scope: links:write.
404 No link with that UUID exists in this workspace.

📊 Responses

Retrieve all responses collected for a specific feedback link.

Query Parameters:

Parameter Type Default Description
page integer 1 Page number.
page_size integer 20 Results per page.
ordering string -submitted_at Sort field.
search string Search in email or transcript.

Response (200 OK):

{
  "count": 42,
  "next": "https://sayify.pro/api/v1/links/{link_uuid}/responses/?page=2",
  "previous": null,
  "results": [
    {
      "uuid": "resp-uuid-1",
      "session_id": "sess-uuid-1",
      "answers": [
        {
          "uuid": "ans-uuid-1",
          "question_text": "Tell us about your experience",
          "audio_url": "https://resource.sayify.pro/responses/abc123/audio.mp3",
          "transcription": "I really love the voice recording feature!",
          "duration": 5.2,
          "ai_insights": {
            "sentiment": "positive",
            "sentiment_score": 0.92,
            "actionable": true,
            "key_topics": ["voice recording", "product feedback"],
            "summary": "User expresses strong satisfaction with voice recording"
          }
        }
      ],
      "respondent_email": "user@example.com",
      "submitted_at": "2026-02-10T13:30:00Z",
      "status": "completed"
    }
  ]
}

Returns the full detail of a single response including all answers and AI insights.


🔔 Webhooks

GET /v1/workspaces/{workspace_uuid}/webhooks/ — List Webhooks

Returns all webhooks configured in your workspace.


POST /v1/workspaces/{workspace_uuid}/webhooks/ — Create Webhook

Field Type Required Description
url string Your webhook endpoint URL (must be HTTPS).
events array Event types: ["response.started", "response.submitted"].
enabled boolean Whether webhook is active (default: true).

DELETE /v1/workspaces/{workspace_uuid}/webhooks/{webhook_uuid}/ — Delete Webhook

Permanently removes the webhook.

[!NOTE]
For detailed webhook payload examples, event documentation, and retry policy, see Webhooks.


🔑 API Tokens

GET /v1/tokens/ — List Tokens

Returns all API tokens in your workspace.

POST /v1/tokens/ — Create Token

{ "name": "Production Key" }

Returns the token with the full API key (shown only once).

DELETE /v1/tokens/{token_uuid}/ — Revoke Token

Immediately revokes the token. Any in-flight requests using this key will fail.


📝 Question Types

Sayify supports 11 response types. Use the type field when creating questions via the API.

Type Description Configuration
voice Voice recording with optional AI evaluation. { max_duration, enable_ai_evaluation }
voice_text_fallback Voice with text fallback if mic unavailable. { max_duration, max_characters }
text Free text input. {}
multiple_choice Single or multi-select from predefined options. { options: [...], allow_multiple }
number Numeric input with optional min/max. { min, max, step }
rating Star, emoji, NPS, or numeric scale. { scale_type, max_value }
image_choice Visual card selection with images/videos. { cards: [{ id, label, media_url, media_type }] }
email Email address input with validation. {}
date Date picker. {}
yes_no Boolean yes/no toggle. {}
file_upload File attachment upload. { allowed_types: [...], max_size_mb }

Configuration Examples

Voice with AI probing:

{
  "prompt": "Tell us about your experience",
  "type": "voice",
  "field_key": "experience",
  "configuration": { "max_duration": 120, "enable_ai_evaluation": true },
  "enable_dynamic_probing": true,
  "evaluator_config": {
    "clarity_threshold": 60,
    "max_follow_ups": 2,
    "custom_fields": ["Summary"]
  }
}

Voice with text fallback:

{
  "prompt": "Describe any issues you faced",
  "type": "voice_text_fallback",
  "field_key": "issues",
  "configuration": { "max_duration": 120, "max_characters": 500 }
}

Multiple choice:

{
  "prompt": "How did you hear about us?",
  "type": "multiple_choice",
  "field_key": "referral_source",
  "configuration": {
    "options": ["Google", "Friend", "Social Media", "Other"],
    "allow_multiple": false
  }
}

Number (NPS):

{
  "prompt": "How likely are you to recommend us? (1-10)",
  "type": "number",
  "field_key": "nps_score",
  "configuration": { "min": 1, "max": 10, "step": 1 }
}

Rating (star/emoji/NPS scale):

{
  "prompt": "Rate your experience",
  "type": "rating",
  "field_key": "experience_rating",
  "configuration": {
    "scale_type": "stars",
    "max_value": 5
  }
}

scale_type options: "stars" (⭐ default), "emojis" (😡😞😐😊🤩), "nps" (0–10), "numbers" (plain numeric).

Webhook payloads include rating_details: { value, max_value, scale_type } for this type.

Image choice (visual cards):

{
  "prompt": "Select your preferred design",
  "type": "image_choice",
  "field_key": "preferred_design",
  "configuration": {
    "cards": [
      {
        "id": "card-uuid-1",
        "label": "Modern Blue",
        "media_url": "https://example.com/blue.jpg",
        "media_type": "image"
      },
      {
        "id": "card-uuid-2",
        "label": "Classic Red",
        "media_url": "https://example.com/red.mp4",
        "media_type": "video"
      }
    ]
  }
}

media_type can be "image" or "video". Webhook payloads include selected_card: { card_id, label, media_url, media_type } for this type.

File upload:

{
  "prompt": "Upload your resume",
  "type": "file_upload",
  "field_key": "resume",
  "configuration": {
    "allowed_types": ["pdf", "docx"],
    "max_size_mb": 5
  }
}

🔀 Conditional Logic

Control question visibility based on previous answers. Add a condition object to any question.

Operator Description Example
less_than Show if numeric value is below threshold. Show "What went wrong?" if rating < 7.
equals Show if value matches exactly. Show "T-shirt size?" if attendance_type == "In-Person".
contains Show if value contains a keyword. Show "Tell us more" if issue_type contains "bug".

Example — Show follow-up if rating is low:

{
  "prompt": "What went wrong?",
  "type": "voice",
  "field_key": "negative_feedback",
  "condition": {
    "show_if": "field_value",
    "field": "rating",
    "operator": "less_than",
    "value": 7
  }
}

Example — Show based on choice:

{
  "prompt": "Your t-shirt size?",
  "type": "multiple_choice",
  "field_key": "tshirt_size",
  "condition": {
    "show_if": "field_value",
    "field": "attendance_type",
    "operator": "equals",
    "value": "In-Person"
  },
  "configuration": {
    "options": ["XS", "S", "M", "L", "XL", "2XL"]
  }
}

Example — Show if answer mentions keyword:

{
  "prompt": "Tell us more about the bug",
  "type": "voice_text_fallback",
  "field_key": "bug_details",
  "condition": {
    "show_if": "field_value",
    "field": "issue_type",
    "operator": "contains",
    "value": "bug"
  }
}

🎓 What's Next?

  • API Overview — Base URL, request format, pagination, rate limits.
  • Authentication — API key creation and security.
  • Errors — Error codes and retry strategy.
  • Webhooks — Event payloads, retry policy, and delivery logs.
Was this page helpful?
Report an issue →