DEVELOPER API

Integrate emotional voice analysis into your product

Send audio, check analysis status, and download the normalized report from your own backend — the same intelligence the Voice Feeling portal uses, exposed as a REST API.

01 / QUICKSTART

From an audio file to a report

Four calls cover the whole flow: create the analysis, check its status, and download the normalized report once it finishes.

  1. Create an analysis by uploading the file

    Send the audio as multipart/form-data. `consentObtained=true` is required: it confirms you have consent or a legal basis to analyze the recording.

    curl https://voice-feeling.pages.dev/api/v1/analyses \
      -H "Authorization: Bearer vf_live_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
      -H "Idempotency-Key: 5f3d2b7e-9c2e-4f6a-9c0e-1a2b3c4d5e6f" \
      -F "file=@call.wav" \
      -F "consentObtained=true" \
      -F "businessContext=sales"
  2. Create an analysis from a URL

    If the audio already lives on your infrastructure, send JSON with `audioUrl` instead of uploading the file. The same `consentObtained: true` is still required.

    curl https://voice-feeling.pages.dev/api/v1/analyses \
      -H "Authorization: Bearer vf_live_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
      -H "Idempotency-Key: 5f3d2b7e-9c2e-4f6a-9c0e-1a2b3c4d5e6f" \
      -H "Content-Type: application/json" \
      -d '{
        "audioUrl": "https://cdn.example.com/calls/2026-09-03-0142.wav",
        "consentObtained": true,
        "businessContext": "service"
      }'
  3. Check the status

    The analysis moves through several states until `completed` or `failed`. Poll with backoff, or register a webhook to be notified instead.

    curl https://voice-feeling.pages.dev/api/v1/analyses/an_9f3c2e1a \
      -H "Authorization: Bearer vf_live_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
    
    # {
    #   "id": "an_9f3c2e1a",
    #   "status": "completed",
    #   "businessContext": "service",
    #   "createdAt": "2026-09-03T14:02:11Z",
    #   "completedAt": "2026-09-03T14:03:47Z",
    #   "reportUrl": "/api/v1/analyses/an_9f3c2e1a/report"
    # }
  4. Download the normalized report

    Only available once the analysis is `completed`. The report follows the `voicefeeling.report.v1` contract and never contains the provider's raw response.

    curl https://voice-feeling.pages.dev/api/v1/analyses/an_9f3c2e1a/report \
      -H "Authorization: Bearer vf_live_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Send an `Idempotency-Key` header on `POST /analyses` so network retries can't duplicate the analysis.

02 / AUTHENTICATION

API keys and scopes

Authenticate every request with an API key in the Authorization header. Create and manage your keys from the portal, in the Developers section.

Required header
Authorization: Bearer vf_live_<prefix>_<secret>
Key format
vf_live_<prefix>_<secret>. The prefix identifies the key in logs; the secret is only shown once when it's created.

Available scopes

Each key declares the scopes it needs. A request missing the required scope gets `api_scope_missing` with the missing scope in the response.

03 / IDEMPOTENCY

Safe retries

Every delivery can repeat: if your network request fails without a clear response, resend it with the same `Idempotency-Key` header. If the key was already used with a different body, the API returns `idempotency_conflict` instead of creating a second analysis.

04 / WEBHOOKS

Event notifications

Instead of polling, register an HTTPS endpoint to receive events when an analysis finishes.

Available events

Payload shape

Each delivery is a JSON POST with identification and signature headers.

POST https://your-domain.com/webhooks/voicefeeling
Content-Type: application/json
Voice-Feeling-Event: analysis.completed
Voice-Feeling-Signature: t=1756900931,v1=4b1f9e...c2a0

{
  "event": "analysis.completed",
  "analysisId": "an_9f3c2e1a",
  "createdAt": "2026-09-03T14:03:47Z"
}

Verify the signature

Compute the HMAC-SHA256 of the timestamp and body with your secret, and compare it to the received signature before trusting the payload. Reject deliveries whose timestamp is more than 5 minutes off from your clock.

const crypto = require("node:crypto");

function isValidSignature(header, rawBody, secret, toleranceSeconds = 300) {
  const parts = Object.fromEntries(
    header.split(",").map((part) => part.split("="))
  );
  const timestamp = Number(parts.t);
  if (!timestamp || Math.abs(Date.now() / 1000 - timestamp) > toleranceSeconds) {
    return false;
  }
  const expected = crypto
    .createHmac("sha256", secret)
    .update(`${timestamp}.${rawBody}`)
    .digest("hex");
  return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(parts.v1 || ""));
}

Retries and disabling

Failed deliveries retry with exponential backoff for about an hour. An endpoint with 20 consecutive failures is disabled automatically; re-enable it by deleting and re-registering it from the portal.

05 / ERRORS

Error codes

Every error response uses the same envelope: `{ "error": { "code", "message", "docs_url" } }`. Build your integration against `code`, not `message`, which can change language based on `Accept-Language`.

CODEHTTPDESCRIPTION
api_key_invalid401The key doesn't exist or doesn't match any registered key.
api_key_expired401The key had an expiration date that has passed.
api_key_revoked401The key was revoked from the portal.
api_scope_missing403The key is missing the required scope; the response includes `required_scope`.
api_rate_limited429The per-key request rate limit was exceeded; respect the `Retry-After` header.
api_disabled503The public API is disabled in this environment.
consent_required422The analysis creation request is missing `consentObtained: true`.
audio_url_invalid422`audioUrl` isn't a valid, public https URL.
audio_download_failed422The audio couldn't be downloaded from `audioUrl`.
unsupported_audio_content415The file's content doesn't match a supported audio format.
audio_too_large413The file exceeds the allowed size limit.
monthly_analysis_quota_exceeded429The plan's monthly analysis quota was reached.
daily_upload_quota_exceeded429The plan's daily upload quota was reached.
storage_quota_exceeded413The organization's storage reached its limit.
idempotency_conflict409The `Idempotency-Key` was already used with a different request body.
analysis_not_found404No analysis with that id exists for this organization.
report_requires_final409The analysis hasn't finished yet; the report isn't available.
webhook_url_invalid422The webhook URL isn't a valid, public https URL.
webhook_endpoint_not_found404No webhook endpoint with that id exists for this organization.
webhook_limit_reached409The maximum number of webhook endpoints per organization was reached.
06 / LIMITS

Technical limits

Design your integration around these limits; exceeding them returns the matching error code, not a silent cutoff.

Request rate
120 requests / 10 minutes per key
Maximum audio size
50 MiB per file
Supported formats
WAV, MP3, M4A, OGG, FLAC, WebM, AAC, AIFF
Responsible use of acoustic signals

Voice Feeling measures acoustic signals (energy, tension, pace) correlated with emotional states. They are not psychological facts, do not determine intent, and are never a truth-or-lie verdict. Any decision affecting a person must combine this with human context and your organization's policies.