For the complete documentation index, see llms.txt. This page is also available as Markdown.

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 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.


The response

Return an HTML fragment.

  • The content type must be text/html or text/plain. A missing content type is tolerated. Anything else is rejected with a 502 and 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:

  • http or https, with https required in production.

  • Port must be the protocol default, or 80, 443, 8080, or 8443.

  • 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 Authorization header 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.

Assistant scoping controls where the panel is offered, not who may call your endpoint. The dashboard is what filters on your selected assistants, so treat scoping as a display setting and put your own authorization checks in your endpoint. Use Enabled when you want an app to stop being served altogether.

Last updated