Duos
Two-person avatars.
/v1/duosList the organisation's duos
Newest first. While duos are closed to the organisation (GET /duos/access builderOpen: false) this answers 404.
Query parameters
limitintegerPage size, 1-200 (default 50).
cursorstringThe
nextCursorof the previous page.
Returns
200 A page of duos. A page: data is a list of Duo, and nextCursor fetches the next page.
Fields of data
idstringrequiredDuo id (
duo_…).pattern `^duo_.*`
personaIdstringrequiredThe persona sessions and portals use (its name is "A & B").
pattern `^pa_.*`
source"library" | "generated" | "composed" | "upload"requiredWhere its picture came from.
libraryKeystring | nullrequiredThe ready-made duo it was copied from.
peopleobject[]requirednamestringrequiredgender"female" | "male"requiredrolestring | nullrequiredTheir part in the conversation, in a few words.
voiceIdstring | nullrequired
relationshipstring | nullrequiredstatus"draft" | "ready_for_render" | "registering" | "testing" | "live" | "needs_changes" | "failed"requireddraft, then ready_for_render once published; registering and testing on the render side; live when its test run passed, or needs_changes / failed.
statusWordsstringrequiredWhere it is, in one plain sentence.
previewUrlstring | nullrequiredIts picture (your API key reads it: it is the organisation's own avatar).
languagesstring[]requiredThe languages a duo speaks (the ten codes GET /languages marks
duo).testReasonsstring[]requiredWhy its test run didn't pass, in plain words (needs_changes, failed).
updatedAtstring (date-time)requiredISO 8601 UTC timestamp.
aiGeneratedtruerequiredAlways true: the duo's video and voices are AI-generated, and so is their picture unless it is a photo you uploaded (EU AI Act Article 50(2)).
Errors
invalid_requestunauthorizedforbiddenupgrade_requirednot_foundrate_limitedserver_error
Request
curl "https://app.zentiqai.com/v1/duos?limit=20" \
-H "Authorization: Bearer $ZENTIQ_API_KEY"import { Zentiq } from "@zentiq/node";
const zentiq = new Zentiq({ apiKey: process.env.ZENTIQ_API_KEY });
const page = await zentiq.listDuos({
limit: 20,
});import os
from zentiq import Zentiq
zentiq = Zentiq(api_key=os.environ["ZENTIQ_API_KEY"])
page = zentiq.list_duos(limit=20)Example answer
{
"data": [
{
"id": "duo_cmfq2b7x10000abcd1234",
"personaId": "pa_cmfq2b7x10000abcd1234",
"source": "library",
"libraryKey": "…",
"people": [
{
"name": "Zoe",
"gender": "female",
"role": "…",
"voiceId": "vo_cmfq2b7x10000abcd1234"
}
],
"relationship": "…"
}
],
"error": null,
"requestId": "req_4f1c2e9a7b3d5e6f7a8b9c0d",
"nextCursor": null
}/v1/duosAdd a ready-made duo
A copy of a ready-made duo, as a draft: its persona ("A & B"), its picture and a voice each. It takes one of the plan's custom-avatar places (403 upgrade_required when there is none) and needs duos on the plan. Publish it to have it tested and go live. While duos are closed to the organisation (GET /duos/access builderOpen: false) this answers 404.
Body
source"library"requiredA copy of one of Zentiq's ready-made duos (your own duos come in a later release).
libraryKeystringrequiredFrom GET /duos/library.
Returns
201 The new duo (draft). data is Duo.
Fields of data
idstringrequiredDuo id (
duo_…).pattern `^duo_.*`
personaIdstringrequiredThe persona sessions and portals use (its name is "A & B").
pattern `^pa_.*`
source"library" | "generated" | "composed" | "upload"requiredWhere its picture came from.
libraryKeystring | nullrequiredThe ready-made duo it was copied from.
peopleobject[]requirednamestringrequiredgender"female" | "male"requiredrolestring | nullrequiredTheir part in the conversation, in a few words.
voiceIdstring | nullrequired
relationshipstring | nullrequiredstatus"draft" | "ready_for_render" | "registering" | "testing" | "live" | "needs_changes" | "failed"requireddraft, then ready_for_render once published; registering and testing on the render side; live when its test run passed, or needs_changes / failed.
statusWordsstringrequiredWhere it is, in one plain sentence.
previewUrlstring | nullrequiredIts picture (your API key reads it: it is the organisation's own avatar).
languagesstring[]requiredThe languages a duo speaks (the ten codes GET /languages marks
duo).testReasonsstring[]requiredWhy its test run didn't pass, in plain words (needs_changes, failed).
updatedAtstring (date-time)requiredISO 8601 UTC timestamp.
aiGeneratedtruerequiredAlways true: the duo's video and voices are AI-generated, and so is their picture unless it is a photo you uploaded (EU AI Act Article 50(2)).
Errors
invalid_requestunauthorizedforbiddenupgrade_requirednot_foundconflictidempotency_conflictidempotency_in_progressrate_limitedserver_error
Request
curl -X POST "https://app.zentiqai.com/v1/duos" \
-H "Authorization: Bearer $ZENTIQ_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"source": "library",
"libraryKey": "…"
}'import { Zentiq } from "@zentiq/node";
const zentiq = new Zentiq({ apiKey: process.env.ZENTIQ_API_KEY });
const result = await zentiq.createDuo({
source: "library",
libraryKey: "…",
});import os
from zentiq import Zentiq
zentiq = Zentiq(api_key=os.environ["ZENTIQ_API_KEY"])
result = zentiq.create_duo({
"source": "library",
"libraryKey": "…",
})Example answer
{
"data": {
"id": "duo_cmfq2b7x10000abcd1234",
"personaId": "pa_cmfq2b7x10000abcd1234",
"source": "library",
"libraryKey": "…",
"people": [
{
"name": "Zoe",
"gender": "female",
"role": "…",
"voiceId": "vo_cmfq2b7x10000abcd1234"
}
],
"relationship": "…"
},
"error": null,
"requestId": "req_4f1c2e9a7b3d5e6f7a8b9c0d"
}/v1/duos/{id}Get a duo
Poll it after publishing: it goes live once its test run has passed. While duos are closed to the organisation (GET /duos/access builderOpen: false) this answers 404.
Path parameters
idstringrequiredDuo id (
duo_…); the id without its prefix is accepted too.
Returns
200 The duo. data is Duo.
Fields of data
idstringrequiredDuo id (
duo_…).pattern `^duo_.*`
personaIdstringrequiredThe persona sessions and portals use (its name is "A & B").
pattern `^pa_.*`
source"library" | "generated" | "composed" | "upload"requiredWhere its picture came from.
libraryKeystring | nullrequiredThe ready-made duo it was copied from.
peopleobject[]requirednamestringrequiredgender"female" | "male"requiredrolestring | nullrequiredTheir part in the conversation, in a few words.
voiceIdstring | nullrequired
relationshipstring | nullrequiredstatus"draft" | "ready_for_render" | "registering" | "testing" | "live" | "needs_changes" | "failed"requireddraft, then ready_for_render once published; registering and testing on the render side; live when its test run passed, or needs_changes / failed.
statusWordsstringrequiredWhere it is, in one plain sentence.
previewUrlstring | nullrequiredIts picture (your API key reads it: it is the organisation's own avatar).
languagesstring[]requiredThe languages a duo speaks (the ten codes GET /languages marks
duo).testReasonsstring[]requiredWhy its test run didn't pass, in plain words (needs_changes, failed).
updatedAtstring (date-time)requiredISO 8601 UTC timestamp.
aiGeneratedtruerequiredAlways true: the duo's video and voices are AI-generated, and so is their picture unless it is a photo you uploaded (EU AI Act Article 50(2)).
Errors
invalid_requestunauthorizedforbiddenupgrade_requirednot_foundrate_limitedserver_error
Request
curl "https://app.zentiqai.com/v1/duos/duo_123" \
-H "Authorization: Bearer $ZENTIQ_API_KEY"import { Zentiq } from "@zentiq/node";
const zentiq = new Zentiq({ apiKey: process.env.ZENTIQ_API_KEY });
const result = await zentiq.getDuo("duo_123");import os
from zentiq import Zentiq
zentiq = Zentiq(api_key=os.environ["ZENTIQ_API_KEY"])
result = zentiq.get_duo("duo_123")Example answer
{
"data": {
"id": "duo_cmfq2b7x10000abcd1234",
"personaId": "pa_cmfq2b7x10000abcd1234",
"source": "library",
"libraryKey": "…",
"people": [
{
"name": "Zoe",
"gender": "female",
"role": "…",
"voiceId": "vo_cmfq2b7x10000abcd1234"
}
],
"relationship": "…"
},
"error": null,
"requestId": "req_4f1c2e9a7b3d5e6f7a8b9c0d"
}/v1/duos/accessWhat the organisation may do with duos
Whether duos are open to the organisation, and what its plan allows: ready-made duos, its own duos.
Returns
200 The organisation's duo access. data is DuoAccess.
Fields of data
builderOpenbooleanrequiredDuos are open to the organisation (while not, every other /duos operation is 404).
canUsebooleanrequiredThe plan uses ready-made duos.
canBuildbooleanrequiredThe plan builds its own duos.
upgradeobject | nullrequiredThe plan that would add what's missing, and the words for it; null when nothing is.
planstringrequiredmessagestringrequired
Errors
invalid_requestunauthorizedforbiddenupgrade_requiredrate_limitedserver_error
Request
curl "https://app.zentiqai.com/v1/duos/access" \
-H "Authorization: Bearer $ZENTIQ_API_KEY"import { Zentiq } from "@zentiq/node";
const zentiq = new Zentiq({ apiKey: process.env.ZENTIQ_API_KEY });
const result = await zentiq.getDuoAccess();import os
from zentiq import Zentiq
zentiq = Zentiq(api_key=os.environ["ZENTIQ_API_KEY"])
result = zentiq.get_duo_access()Example answer
{
"data": {
"builderOpen": true,
"canUse": true,
"canBuild": true,
"upgrade": {
"plan": "…",
"message": "…"
}
},
"error": null,
"requestId": "req_4f1c2e9a7b3d5e6f7a8b9c0d"
}/v1/duos/libraryZentiq's ready-made duos
The ready-made duos an organisation can copy (POST /duos), in library order. While duos are closed to the organisation (GET /duos/access builderOpen: false) this answers 404.
Returns
200 The library. data is a list of LibraryDuo.
Fields of data
keystringrequiredWhat POST /duos takes as
libraryKey.titlestringrequiredscenariostring | nullrequiredpeopleobject[]requiredThe person on the left, then the one on the right.
namestringrequiredgender"female" | "male"requiredrolestring | nullrequiredTheir part in the conversation, in a few words.
relationshipstring | nullrequiredcouple, colleagues, family or panel.
previewUrlstringrequiredTheir picture (public).
aiGeneratedtruerequiredAlways true: the duo's video and voices are AI-generated, and so is their picture unless it is a photo you uploaded (EU AI Act Article 50(2)).
Errors
invalid_requestunauthorizedforbiddenupgrade_requirednot_foundrate_limitedserver_error
Request
curl "https://app.zentiqai.com/v1/duos/library" \
-H "Authorization: Bearer $ZENTIQ_API_KEY"import { Zentiq } from "@zentiq/node";
const zentiq = new Zentiq({ apiKey: process.env.ZENTIQ_API_KEY });
const result = await zentiq.listDuoLibrary();import os
from zentiq import Zentiq
zentiq = Zentiq(api_key=os.environ["ZENTIQ_API_KEY"])
result = zentiq.list_duo_library()Example answer
{
"data": [
{
"key": "second_viewing",
"title": "Mark & Susan",
"scenario": "…",
"people": [
{
"name": "Zoe",
"gender": "female",
"role": "…"
}
],
"relationship": "…",
"previewUrl": "https://www.example.com"
}
],
"error": null,
"requestId": "req_4f1c2e9a7b3d5e6f7a8b9c0d"
}/v1/duos/{id}/publishPublish a duo
Sends it to be set up and tested: it becomes ready_for_render, then live once its short test run (each person says a line) has passed, or needs_changes. 409 says what is still missing; a second publish while it is on its way changes nothing; a live duo is published again only after a change. Sessions and portals take it once it is live. While duos are closed to the organisation (GET /duos/access builderOpen: false) this answers 404.
Path parameters
idstringrequiredDuo id (
duo_…); the id without its prefix is accepted too.
Returns
200 The duo, on its way. data is Duo.
Fields of data
idstringrequiredDuo id (
duo_…).pattern `^duo_.*`
personaIdstringrequiredThe persona sessions and portals use (its name is "A & B").
pattern `^pa_.*`
source"library" | "generated" | "composed" | "upload"requiredWhere its picture came from.
libraryKeystring | nullrequiredThe ready-made duo it was copied from.
peopleobject[]requirednamestringrequiredgender"female" | "male"requiredrolestring | nullrequiredTheir part in the conversation, in a few words.
voiceIdstring | nullrequired
relationshipstring | nullrequiredstatus"draft" | "ready_for_render" | "registering" | "testing" | "live" | "needs_changes" | "failed"requireddraft, then ready_for_render once published; registering and testing on the render side; live when its test run passed, or needs_changes / failed.
statusWordsstringrequiredWhere it is, in one plain sentence.
previewUrlstring | nullrequiredIts picture (your API key reads it: it is the organisation's own avatar).
languagesstring[]requiredThe languages a duo speaks (the ten codes GET /languages marks
duo).testReasonsstring[]requiredWhy its test run didn't pass, in plain words (needs_changes, failed).
updatedAtstring (date-time)requiredISO 8601 UTC timestamp.
aiGeneratedtruerequiredAlways true: the duo's video and voices are AI-generated, and so is their picture unless it is a photo you uploaded (EU AI Act Article 50(2)).
Errors
invalid_requestunauthorizedforbiddenupgrade_requirednot_foundconflictidempotency_conflictidempotency_in_progressrate_limitedserver_error
Request
curl -X POST "https://app.zentiqai.com/v1/duos/duo_123/publish" \
-H "Authorization: Bearer $ZENTIQ_API_KEY" \
-H "Idempotency-Key: $(uuidgen)"import { Zentiq } from "@zentiq/node";
const zentiq = new Zentiq({ apiKey: process.env.ZENTIQ_API_KEY });
const result = await zentiq.publishDuo("duo_123");import os
from zentiq import Zentiq
zentiq = Zentiq(api_key=os.environ["ZENTIQ_API_KEY"])
result = zentiq.publish_duo("duo_123")Example answer
{
"data": {
"id": "duo_cmfq2b7x10000abcd1234",
"personaId": "pa_cmfq2b7x10000abcd1234",
"source": "library",
"libraryKey": "…",
"people": [
{
"name": "Zoe",
"gender": "female",
"role": "…",
"voiceId": "vo_cmfq2b7x10000abcd1234"
}
],
"relationship": "…"
},
"error": null,
"requestId": "req_4f1c2e9a7b3d5e6f7a8b9c0d"
}