Skip to main content
Read the pull → edit → push walkthrough first if you haven’t already. This page picks up from there: which fields turn a Digital Human into a voicemail line, a customer journey, or a transcript replay, and the handful of field names Bluejay as Code renames from the API. Full field lists: create-digital-human, add-agent, create-custom-metric.

Field Names That Differ From the API

Bluejay as Code uses a few friendlier names in the payload than the underlying resource does: You can also set a Digital Human’s voice with one combined key instead of three. "accent": "en_american_masculine" follows the pattern language_accent_gender, with gender spelled masculine or feminine. Bluejay splits it into language, accent, and gender on push. Pull always emits the three separate fields, so this shorthand only matters when you’re hand-writing a new Digital Human.

Choosing a Digital Human Type

A Digital Human’s tag decides its type. The one exception is customer journeys, which use journey_steps instead. Pick one type per Digital Human. Each example below omits simulations; add it to attach the Digital Human to one, as shown on the walkthrough.
Set tag to voicemail-custom or voicemail-ai-generated.
If tag carries more than one label, put the voicemail tag first. Bluejay only checks the first label.
formatted_transcript replays a real call instead of description/success_criteria. original_transcript takes raw text instead and costs one LLM call to extract the turns.
A customer journey runs multiple calls with the same caller in order.
A Digital Human tagged Scenario is generated by Scenario Builder.
Don’t write enriched_playback by hand. It won’t stay in sync with the graph.
A Digital Human generated by a red teaming run carries attack_vector, attack_type, and a much larger attack_plan. Launch these through red teaming, not Bluejay as Code.
livekit_metadata is also a generated field: agent connection plumbing set through the agent’s LiveKit connection settings, not written by hand.

Agents, Simulations, and Custom Metrics

These three don’t have archetypes the way Digital Humans do, so there’s one example each instead of an accordion per variant.

Agents

Every field here is exactly what add-agent takes.

Simulations

Every field here is exactly what update-simulation takes. A simulation has no agent field of its own: a new simulation’s owner comes from whichever agent’s simulations array lists its bluejay_as_code_id, or from the bundle’s only agent when there’s just one. An existing simulation’s owner can’t be reassigned on push.

Custom Metrics

Every field here is exactly what create-custom-metric takes. metric_type decides which other fields apply — tool_call and the audio_* types read from settings instead of prompt.

Conditional and Composite Metrics

Two kinds of custom metric are configured entirely through settings, so they don’t appear in the create-custom-metric field list. A conditional metric only runs on conversations that match a condition you set. On every other call it comes back not applicable instead of failing, so a metric about insurance doesn’t drag down calls that never discussed insurance. A composite metric doesn’t judge the conversation at all. It reads the scores of other metrics on the same call and turns them into a number or a label. There’s no LLM call, so it costs nothing to run. The examples below continue the Cedar Park Family Medicine agent from the walkthrough, and read the two metrics already in that payload plus one more, Scheduling Clarity, a 1-5 score on how clearly the agent handled the booking.
Add fire_conditions to any metric’s settings. Conditions are grouped: the conditions inside a group are joined by that group’s join, and the groups are joined by the outer one. Both joins default to and, so a single condition needs nothing but groups.This metric only runs on calls that reached the agent’s insurance step.
Get your agent’s node IDs from GET /v1/agents/{agent_id}/workflow/summary.A condition’s type is one of three:A metric condition’s operator is one of equals, not_equals, greater_than, less_than, greater_than_or_equal, less_than_or_equal, contains, or not_contains. You can mix types freely, up to ten conditions across all groups.
An older metric you pull might have settings.fire_node_ids instead — a flat list of node IDs. It still works, but it can’t express groups or the other two condition types. Write fire_conditions for anything new.
Set metric_type to composite and output_type to quantitative. List the metrics it reads in variables, then combine them in master_formula.Refer to a metric by its ID in double quotes. That’s the only way to name one, and it means the formula keeps working when someone renames a metric. Formulas are arithmetic only — +, -, *, /, %, **, and the functions min, max, abs, round, floor, and ceil.A metric that isn’t already a number needs a value_map to become one. Here a pass on Appointment Details Confirmed counts as 5 and a fail counts as 1.
Each result saves its own arithmetic in reasoning, so you can always see where a score came from:
Set output_type to categorical and replace the formula with rules. Each rule gives an output and the conditions that produce it, grouped the same way fire_conditions are. Conditions here compare a declared metric against a number, so pass/fail inputs still need a value_map.Rules are checked in order and the first one that holds wins, so put the strictest first. If no rule holds, the metric is not applicable — there’s no fallback label. You get up to ten rules, each with up to ten conditions.
A rule can only read a metric that variables already lists.
Metrics that read other metrics stay one level deep. A conditional metric might produce no result at all, so nothing can be built on top of one. Pointing a fire condition or a composite variable at a conditional metric is refused with a 422, and so is making a metric conditional or composite when other metrics already read it. Point at a metric that always runs instead.
One thing to watch on push: a malformed payload, like a bad output_type or a formula reading a metric that variables doesn’t list, comes back as 200 with valid: false and an errors array, not as a 4xx. Check valid on every push rather than the status code alone.

Wiring them together

A simulation’s digital_humans, an agent’s custom_metrics, and a custom metric’s agents are all desired-state arrays, not append-only lists. Push replaces each one with exactly what you listed. Drop an id that was there before, and Bluejay unlinks it without deleting the object itself. See reconciliation in the walkthrough for the full behavior.