Custom Apps
A custom app renders your own HTML in a panel beside a conversation in the Yedric dashboard. When a teammate opens a conversation, Yedric posts the visitor's context to a URL you host and renders what you return.
Create and scope custom apps in Organization > Integrations > Custom App. See Integrations for that walkthrough. This page is the contract your endpoint has to satisfy.
The request
Yedric's backend makes the call, not the browser, so your secret is never exposed to the dashboard and your endpoint does not need CORS headers.
POST https://example.com/yedric-sidebar
Content-Type: application/json
Authorization: a1b2c3d4e5f6β¦
{
"username": "customer@example.com",
"context": {
"sessionId": "sess_β¦",
"agentId": "agt_β¦"
}
}username is the end user's identity for the conversation. In secure mode this is the signed username you provided. It is an empty string when the conversation has no identified user.
context carries the ids for whatever the teammate is looking at, and the keys depend on the surface. From the Conversations view you get sessionId and agentId. From a live chat you get chatId and agentId instead. Only agentId is present in both, so treat the rest as optional and branch on which id you received rather than assuming sessionId is always there.
The request times out after 10 seconds.
Verifying the request
The Authorization header is the hex HMAC-SHA256 of the username, keyed with the secret you saved on the app:
Recompute that value on your side and compare it against the header using a constant-time comparison, rather than a plain string equality check. Treat a missing or non-matching header as an unauthenticated request.
This signs the username only, not the request body. It tells you the call came from Yedric and which user it concerns. It does not authenticate the rest of the payload, so do not treat context as tamper-proof. Look records up by the ids you are given rather than trusting values inside them.
This is the same signing scheme used by Secure Mode, so if you already sign usernames there, you can reuse that code.
Signing cannot be turned off
Leaving the Secret field blank does not stop Yedric from signing. The Authorization header is always sent. A blank secret simply means the HMAC is keyed with an empty string, which is a value anybody can reproduce.
Two things follow, and both matter. Never treat a missing Authorization header as a trusted internal call, because Yedric never omits it: a request arriving without one did not come from us. And never leave the secret blank on an endpoint that returns anything private, because the signature is then forgeable by anyone who knows your customer's username. Set a real secret before you point an app at real data.
The response
Return an HTML fragment.
The content type must be
text/htmlortext/plain. A missing content type is tolerated. Anything else is rejected with a502and the panel shows an error.The body is capped at 2 MB. Over the cap, Yedric renders the first 2 MB and shows a note that the rest was not loaded, rather than failing outright. Aim well under it.
Return a fragment, not a full document. Anything outside the body is discarded.
How your HTML is rendered
Your markup is sanitized with DOMPurify and injected into a Shadow DOM root. That has two consequences worth designing around:
Your styles are isolated in both directions. The dashboard's CSS will not leak into your panel, and yours will not leak out. Inline styles and a
<style>block inside your fragment work fine.Scripts do not run. Sanitization strips them. The panel is for displaying information and linking out, not for interactive widgets.
Every link that is not a same-page fragment anchor is rewritten to target="_blank" and rel="noopener noreferrer", so clicking one opens a new tab rather than navigating your teammate away mid-conversation.
A small default stylesheet is applied so plain markup looks reasonable without any work from you.
URL requirements
The same rules as webhook callback URLs apply. They are checked when you save the app and again on every call, including after each redirect:
httporhttps, withhttpsrequired in production.Port must be the protocol default, or
80,443,8080, or8443.The hostname must resolve to a public IP address. Private, loopback, link-local, and reserved ranges are refused.
Redirects are re-validated at each hop. Your
Authorizationheader is not carried across a cross-origin redirect, so answer at the URL you configured.
Because loopback and private addresses are refused, your endpoint has to be reachable from the public internet before the panel will load. There is no way to point a custom app at a service running on your own machine. Put it behind a tunnel that gives you a public https URL while you are building.
Notes on the secret
Unlike a webhook signing secret, a custom app secret is stored so that it can be reused, and is masked as *** whenever the app is read back. There is no reveal and no rotate flow. To change it, type a new value into the Secret field and save. Sending ***, or omitting the field, keeps the value you already have.
Turning an app off
The Enabled switch stops the panel appearing for everyone immediately. A disabled app is also refused at the proxy, so an already-open dashboard tab or a saved id cannot keep fetching it.
Scoping matters too: a custom app with no assistants selected appears nowhere. This is the opposite of webhooks, where selecting none means all. If your panel is not showing up, check the Assistants boxes first.
Last updated