curl --request POST \
--url https://{host}/api/v1/ivr-flows/{id}/designer-session \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"return_origin": "https://app.example.com",
"panel_origin": "<string>",
"ttl_minutes": 120
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
return_origin: 'https://app.example.com',
panel_origin: '<string>',
ttl_minutes: 120
})
};
fetch('https://{host}/api/v1/ivr-flows/{id}/designer-session', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://{host}/api/v1/ivr-flows/{id}/designer-session"
payload = {
"return_origin": "https://app.example.com",
"panel_origin": "<string>",
"ttl_minutes": 120
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"expires_at": "2023-11-07T05:31:56Z",
"flow_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"url": "<string>"
}{
"error": {
"code": "invalid_request",
"message": "<string>"
}
}{
"error": {
"code": "invalid_request",
"message": "<string>"
}
}{
"error": {
"code": "invalid_request",
"message": "<string>"
}
}{
"error": {
"code": "invalid_request",
"message": "<string>"
}
}{
"error": {
"code": "invalid_request",
"message": "<string>"
}
}Get a one-time link to the IVR designer
For an application that manages IVRs through the API and lets its own users design them. An IVR is drawn in FireTone’s designer, not posted as JSON; this returns a URL that opens that designer on this one IVR, with no FireTone sign-in.
Call it from your server (it needs your API key), then open url in a popup from the page at return_origin.
The link works once. The designer trades it for a session as it loads. That session can read, save and publish this IVR, and read the lists its steps choose from (agents, queues, extensions, recordings, templates). It can do nothing else, and it ends at expires_at, when the user presses Done, or when the API key that asked for it is revoked.
What the designer tells your page. It posts messages to the window that opened it, addressed to return_origin:
{"source":"firetone","type":"ivr.saved" | "ivr.published" | "ivr.closed","flow_id":"…","name":"…","draft_revision":3,"published_revision":2}
Check event.origin is your FireTone panel’s address before trusting one, and read the IVR back with GET /ivr-flows/{id} rather than acting on the message alone.
409 no_panel_address when the platform has no panel address to open.
curl --request POST \
--url https://{host}/api/v1/ivr-flows/{id}/designer-session \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"return_origin": "https://app.example.com",
"panel_origin": "<string>",
"ttl_minutes": 120
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
return_origin: 'https://app.example.com',
panel_origin: '<string>',
ttl_minutes: 120
})
};
fetch('https://{host}/api/v1/ivr-flows/{id}/designer-session', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://{host}/api/v1/ivr-flows/{id}/designer-session"
payload = {
"return_origin": "https://app.example.com",
"panel_origin": "<string>",
"ttl_minutes": 120
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"expires_at": "2023-11-07T05:31:56Z",
"flow_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"url": "<string>"
}{
"error": {
"code": "invalid_request",
"message": "<string>"
}
}{
"error": {
"code": "invalid_request",
"message": "<string>"
}
}{
"error": {
"code": "invalid_request",
"message": "<string>"
}
}{
"error": {
"code": "invalid_request",
"message": "<string>"
}
}{
"error": {
"code": "invalid_request",
"message": "<string>"
}
}Authorizations
Every request sends Authorization: Bearer <token>. The token is either a panel session (a JWT from /auth/login, 12 hours) or an API key ft_<id>_<secret>. An API key is accepted only from an address on its IP allowlist (403 ip_not_allowed otherwise; 403 ip_allowlist_required for an old key that has none), is limited to its rate per minute (429 rate_limited with Retry-After; X-RateLimit-Limit/Remaining/Reset on every response), and at most 60 call placements a minute.
Path Parameters
Body
The origin of the page that opens the designer: scheme, host and port, nothing else. The designer reports to this origin only (postMessage), so it must be exact. https, or http://localhost while developing; an origin the platform already admits browsers from is accepted as it is listed. A wildcard is refused.
"https://app.example.com"
Rarely needed. Which of the platform's panel addresses the link should open, when it has several; it must be one of them.
How long the link, and the session it becomes, lasts.
5 <= x <= 480Response
The link
When the link, and the designer session, stops working.
The IVR the designer will open.
Open this in a popup (window.open). It works once: the designer trades it for a session as it loads, so a copied or reloaded link shows an expired message. The secret is in the URL fragment and is never sent to a server.