Non-verbal tags

Drop a bracketed token into your input text and Hakim Faseeh / Mulhim weave the cue into the audio. Useful for podcasts, voiceover-heavy ads, voicebot persona work, and anywhere the absence of a sigh or a chuckle would read as flat.

curl https://api.tryhakim.ai/v1/audio/speech \
  -H "Authorization: Bearer $HAKIM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "hakim-v2",
    "input": "Wait. Did that just happen? [laugh] Honestly, [breath] I have no words.",
    "voice": "layla-egyptian"
  }' \
  --output reaction.mp3

Token reference

TokenEffect
[laugh]Laughter
[breath]Sigh / exhale
[think]Hesitation, "uhm"
[whisper]Soft / hushed delivery
[ask]Question intonation
[wow]Surprise
[hmph]Grumble / dissatisfaction

The set is intentionally small · every token has been tuned across the supported voice catalogue to land naturally rather than as a stage cue. If you want a richer non-verbal palette, open an issue describing the cue you're trying to land; we expand the table when there's reliable demand.

Capability gating

TierBehaviour
hakim-fast-v1 (Saree')Tags are stripped before synthesis; the response carries x-hakim-warnings: tags_stripped_by_model_capability. Audio plays clean.
hakim-v2 (Faseeh)Tags from the table above are honoured. Anything outside the table (typo, custom token, stale form from another vendor) is stripped + flagged with the same warning header.
hakim-v3 (Mulhim)Same allowlist as Faseeh.

The strip is silent and graceful · the request still returns 200, the audio still plays, the only signal is the x-hakim-warnings header. Sniff the header presence as a yes/no check; sniff the comma list to fan out per-code remediation.

Best practices

  • Use sparingly. One or two tags per sentence reads natural; five reads as a stage direction. The model is good but not magic.
  • Match the surrounding tone. A [laugh] after "I'm telling you, the meeting was brutal" lands; the same tag after "Please confirm your account number" doesn't.
  • Mix with prose. Hello.[laugh] How are you? works; [laugh][laugh][laugh] doesn't. The model wants context to anchor the cue.
  • Test on the playground first. /app/playground/tts ships a tag-insert chip rail under the composer; clicking inserts the token at the caret so you don't have to memorise the spelling.

Migrating from another vendor's tag set

If you're porting from a system that uses different bracketed tokens ([Question-ah], [Surprise-yo], etc.), Hakim's parser silently strips them · you'll hear the prose without the cue and see the warning header fire. See the tag-map recipe in the models guide for the canonical mapping table; the closest correspondences are usually obvious.

Cookbook

Recipe · Podcast intro with three tag types

Tier: Faseeh v2 (hakim-v2) or Mulhim v3 (hakim-v3). The same script on Saree' would strip every tag silently and play as plain narration.

curl https://api.tryhakim.ai/v1/audio/speech \
  -H "Authorization: Bearer $HAKIM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "hakim-v2",
    "input": "Welcome back. [breath] So. [think] Where do I even start? [laugh] Right. Let'\''s do this.",
    "voice": "layla-egyptian",
    "response_format": "mp3"
  }' \
  --output intro.mp3

What lands:

  • [breath] after "Welcome back." · a deliberate exhale before the first beat. Adds the "the host is settling in" feel.
  • [think] after "So." · a soft "uhm"-like hesitation. Reads as the host searching for the right opening line.
  • [laugh] before "Right." · a brief amused exhale, like the host catching themselves. Lands the energy reset.

Three tags over six sentences is the upper bound of "feels natural" in our testing. Beyond that the audio starts reading as a stage direction; tags are flavour, not dialogue.

Recipe · Tag tone match to dialect

Tier: Faseeh / Mulhim. Tags inherit the voice's emotional register, so the same [laugh] reads differently on each:

Voice[laugh] registerPairs with
layla-egyptianbright, energeticcomedy intros, contemporary fiction
mohammed-msareserved, dry chucklenews commentary, business podcasts
aya-levantinewarm, conversationallifestyle content, interviews
omar-khaleejimeasured, formalcorporate narration, documentaries

Pick the voice for the show's identity; the tag set rides along with it naturally. Don't try to "force" a register that fights the voice · the mismatch is audible.

Recipe · A/B prosody with and without tags

When you're A/B testing whether tags lift listener retention, render both versions on the same Faseeh request batch and serve them randomly:

import { HakimClient } from '@hakim/node';

const hakim = new HakimClient({ apiKey: process.env.HAKIM_API_KEY });

async function renderBoth(script: string, voice: string) {
  // Version A: tagged
  const tagged = await hakim.audio.speech.create({
    model: 'hakim-v2',
    input: script,
    voice,
    response_format: 'mp3',
  });

  // Version B: plain (same model, same voice, same input minus tags)
  const plain = await hakim.audio.speech.create({
    model: 'hakim-v2',
    input: script
      .replace(/\[[a-z]+\]/g, '')
      .replace(/\s+/g, ' ')
      .trim(),
    voice,
    response_format: 'mp3',
  });

  return { tagged, plain };
}

Measure listen-through rate, not first-byte latency · the renders are identical on the wire from a TTFB perspective; the difference is qualitative in the audio itself.