דילוג לתוכן הראשי

SSO login

Your users already work in your product. SSO login lets them reach Teloring from inside it, without a second password: your backend asks for a link, your user opens it, and they land in the console signed in as a specific agent.

Two shapes, for two different jobs:

Full system — the whole console, exactly as if they had signed in. Use it for a "Open in Teloring" button.

Specific conversation — one contact's chat and nothing else. No navigation, no conversation list, no dashboard. Use it when your user is looking at a customer in your system and wants to message them: open it in an iframe, send the message, close the iframe.

Before it will work

The sso scope alone grants nothing. In Settings → API, on the credential, you must also set:

Which agents it may sign in as. A multi-select. An agent not on the list answers 403 agent_not_allowed, and the error lists who is allowed. An empty list means the credential can impersonate nobody — the safe default when somebody ticks the scope and saves without choosing.

Which origins may embed it, if you plan to use an iframe. As many as you need, one per line, scheme and host only:

https://app.example.com
https://portal.example.co.il
https://crm.example.com:8443

Both are editable later without rotating the secret.

Full system

curl -X POST https://api.teloring.com/v1/sso/login \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"agent_id": "agent_12", "mode": "full"}'
{
"object": "sso_login",
"url": "https://console.teloring.com/sso/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9…",
"mode": "full",
"agent_id": "agent_12",
"agent_name": "Dana Levi",
"embed": false,
"expires_at": "2026-08-20T10:34:31+00:00",
"expires_in": 600,
"single_use": true
}

Redirect the browser to url. They land on the dashboard, signed in as Dana.

Specific conversation

Identify the contact by phone or email. Add inbox_id when you know which channel to use.

curl -X POST https://api.teloring.com/v1/sso/login \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "agent_12",
"mode": "conversation",
"contact": { "phone": "+972501234567" },
"inbox_id": "3",
"embed": true
}'

What your user sees depends on what exists, and the API tells you in advance under target:

SituationWhat opens
A conversation with that contact is already openThat conversation, ready to reply
No conversation, and you passed inbox_idThe composer, contact and inbox already chosen — only the message is left (or the template, on WhatsApp outside the 24-hour window)
No conversation, and no inbox_idThe composer with the contact chosen; your user picks the inbox

The contact must already exist. An unknown phone or email answers 404 contact_not_found — create the contact, or start the conversation with POST /v1/conversations, which will create it for you.

Embedding in your own product

Set embed: true and open the URL in an iframe:

<iframe
src="https://console.teloring.com/sso/eyJhbGciOi…"
style="width: 100%; height: 640px; border: 0;"
allow="microphone">
</iframe>

Three things happen because you asked for an embed:

  • The pages are served with Content-Security-Policy: frame-ancestors naming only the origins you configured. Any other site framing the same URL gets nothing.
  • The session cookie is issued SameSite=None; Secure; Partitioned. Partitioned matters: the cookie lives in your site's cookie partition, so it cannot be replayed from anywhere else — which is also what keeps cross-site request forgery closed on an embedded session.
  • In conversation mode, the embedded page posts a message to the parent window when the agent sends something, so you can close the iframe without polling:
window.addEventListener('message', (event) => {
if (event.origin !== 'https://console.teloring.com') return;
if (event.data?.source === 'teloring' && event.data.type === 'message_sent') {
closeTeloringPanel(); // your code
}
});

Requesting embed: true with no origins configured answers 400 no_embed_origins rather than quietly handing you a page no browser will frame.

A URL that bypasses the login screen is a credential, so it is treated like one.

Ten minutes, one use. The link expires after ten minutes and is redeemable exactly once. A second attempt — a copied URL, a duplicated tab, a prefetching proxy — answers 409. Mint one per use; never cache them.

Re-checked at redemption. The link carries no permissions of its own. When it is opened, the credential is re-read (still active? still holds sso? still allows this agent?) and the agent is re-read (still exists? still active?). So removing an agent from the allowlist, or revoking the credential, kills every link already issued — within the ten-minute window, not after it.

Fails closed. If the one-time-use store is unreachable, redemption is refused rather than allowed. A login-bypass link that silently becomes replayable is worse than one that briefly stops working.

Audited. Every link minted and every redemption is written to the audit log with the credential, the agent and the mode, so agent.sso_login in the audit trail always names who let it happen.

If the link is expired, already used, or its credential or agent no longer qualifies, the user sees a small Teloring error page — not a redirect to the sign-in form. That is deliberate: inside your iframe, a Teloring login form is both useless and confusing to somebody who has no Teloring account of their own.

That page says one of four things, and they split into two groups:

What the user seesHTTPCauseDoes a new link fix it?
You have already opened this link409The link was redeemed once already — a refresh, a back button, or a second clickYes
This sign-in link has expired401More than 10 minutes passed between minting and openingYes
This sign-in link is no longer authorised403The credential was revoked, lost the sso scope, or the agent was removed from its allow-listNo
This sign-in link no longer works403The agent it signs in as was deactivatedNo

The first two are the everyday ones, and the page tells the user to go back and open it again — so they resolve themselves as soon as your product mints a new link. The last two are configuration changes on the Teloring side, so the page sends the user to whoever administers the account instead of round a retry loop that cannot succeed.

Your integration's job is simply to mint a fresh link. Because they are cheap and single-use, the right pattern is to request one at the moment the user clicks, never to store one. A refresh of your own page should mint a new link rather than reload the iframe's existing URL — that URL is spent, and reloading it is the single most common way to land on the "already opened" page.

A complete example

def open_teloring_chat(agent_id, customer_phone, inbox_id=None):
"""Return an iframe URL for messaging this customer. Call it per click."""
payload = {
"agent_id": agent_id,
"mode": "conversation",
"contact": {"phone": customer_phone},
"embed": True,
}
if inbox_id:
payload["inbox_id"] = inbox_id

response = requests.post(
"https://api.teloring.com/v1/sso/login",
headers=client._auth_header(),
json=payload,
timeout=10,
)

if response.status_code == 404:
# No such contact yet — create the conversation, which creates the
# contact, then try again.
return None
response.raise_for_status()
return response.json()["url"]

Security notes for your side

  • Mint links server-side only. Your sso credential must never reach the browser: anyone holding it can sign in as any allow-listed agent.
  • Authorise on your side first. Teloring checks that the credential may impersonate the agent. It cannot check that this particular user of yours should be allowed to — that is your call, and you must make it before minting.
  • Map your users to agents deliberately. A shared agent id means your audit log says "the API" where it should say a person.
  • Keep the origin list tight. Every origin you add is a site that may frame a signed-in Teloring session.