For AI agents: a documentation index is available at the root level at /llms.txt and /llms-full.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.
GitHubGet Support
DocsGuidesAPI Reference
DocsGuidesAPI Reference
  • Get started
    • Overview
    • Partner Setup
    • Quickstart
  • Guides
    • Authentication
    • Profile Sync
    • Artifacts
    • Key Concepts
  • Changelog
    • Changelog
LogoLogo
GitHubGet Support
On this page
  • What are artifacts?
  • How artifacts are delivered
  • Parsing embedded artifacts
  • Artifact structure
  • Doctor profile artifacts
  • Parsing doctor profiles from message text
  • Provider search results artifacts
  • Parsing provider search results
  • Scheduling progress artifact
  • Rendering artifacts
  • Embedded artifact format
  • Error handling
  • Malformed JSON
  • Best practices
  • Next steps
Guides

Artifacts

Parse and render rich content delivered inline
||View as Markdown|
Was this page helpful?
Previous

Profile Sync

Next

Key Concepts

Built with

Artifacts are rich content objects embedded in chat messages. Learn how the WebSocket delivers them JIT and how to parse and render artifacts like doctor profiles and custom data structures.

What are artifacts?

Artifacts are structured data objects that provide additional context or information beyond plain text messages. Common examples include:

  • Doctor profiles: Healthcare provider information with NPI, specialty, ratings, etc.
  • Provider search results: Lists of providers from search queries with locations and distances
  • Structured data: Custom data objects specific to your use case
  • Rich content: Any JSON-serializable object

How artifacts are delivered

Artifacts are not fetched via REST API. The WebSocket backend expands artifact tags just-in-time (JIT) during streaming. When the AI references an artifact, the backend replaces the tag with the full artifact JSON and embeds it directly in message.text.

You receive artifact content inline—no separate fetch is needed. The message text contains embedded JSON objects with item_type, item_content, and created_at.

Parsing embedded artifacts

Parse message.text for JSON objects with item_type and item_content:

1function parseEmbeddedArtifacts(text: string): Array<{ item_type: string; item_content: unknown }> {
2 const artifacts: Array<{ item_type: string; item_content: unknown }> = [];
3 let i = 0;
4
5 while (i < text.length) {
6 const start = text.indexOf('{', i);
7 if (start === -1) break;
8
9 let braceCount = 0;
10 let end = start;
11 let inString = false;
12 let escapeNext = false;
13
14 for (let j = start; j < text.length; j++) {
15 const char = text[j];
16 if (escapeNext) { escapeNext = false; continue; }
17 if (char === '\\') { escapeNext = true; continue; }
18 if (char === '"') { inString = !inString; continue; }
19 if (inString) continue;
20 if (char === '{') braceCount++;
21 else if (char === '}') {
22 braceCount--;
23 if (braceCount === 0) { end = j + 1; break; }
24 }
25 }
26
27 if (braceCount === 0 && end > start) {
28 try {
29 const obj = JSON.parse(text.slice(start, end));
30 if (obj && typeof obj === 'object' && obj.item_type && obj.item_content) {
31 artifacts.push({ item_type: obj.item_type, item_content: obj.item_content });
32 }
33 } catch { /* skip invalid JSON */ }
34 i = end;
35 } else {
36 i = start + 1;
37 }
38 }
39 return artifacts;
40}

Artifact structure

The inline JSON format embedded in message text:

1interface InlineArtifact<T = unknown> {
2 item_type: string;
3 item_content: T;
4 created_at?: string;
5}

Doctor profile artifacts

Doctor profiles are a common artifact type:

1interface DoctorProfileArtifact {
2 npi: string;
3 first_name?: string;
4 last_name?: string;
5 title?: string;
6 specialty?: string;
7 languages_spoken?: string[];
8 gender?: string;
9 rating?: number;
10 review_count?: number;
11 last_updated_at?: string;
12 locations?: unknown;
13 rank_score?: number;
14 mrf_rates?: Array<Record<string, unknown>>;
15 out_of_pocket_costs?: Array<{
16 procedure_code: string;
17 procedure_code_type?: string;
18 procedure_name?: string;
19 rate?: number;
20 out_of_pocket: number;
21 }>;
22}

Parsing doctor profiles from message text

1const artifacts = parseEmbeddedArtifacts(message.text);
2
3artifacts.forEach(({ item_type, item_content }) => {
4 if (item_type === "doctor_profile") {
5 const profile = item_content as DoctorProfileArtifact;
6 console.log("Name:", `${profile.first_name} ${profile.last_name}`);
7 console.log("Specialty:", profile.specialty);
8 console.log("Rating:", profile.rating);
9 console.log("NPI:", profile.npi);
10 }
11});

Provider search results artifacts

The provider_search_results artifact type contains search results from the provider search tool:

1interface ProviderSearchResultsArtifact {
2 providers: ProviderResult[];
3 query: string;
4 location: string;
5 taxonomy_codes: string[];
6 plan_name: string;
7 filter_gender: string | null;
8 filter_languages: string[] | null;
9}
10
11interface ProviderResult {
12 npi: string;
13 name: string | null;
14 specialties: string[];
15 degrees: string[];
16 languages: string[];
17 locations: LocationResult[];
18}
19
20interface LocationResult {
21 name: string | null;
22 address_line_1: string | null;
23 address_line_2: string | null;
24 city: string | null;
25 state: string | null;
26 zip: string | null;
27 distance_miles: number | null;
28}

Parsing provider search results

1const artifacts = parseEmbeddedArtifacts(message.text);
2
3artifacts.forEach(({ item_type, item_content }) => {
4 if (item_type === "provider_search_results") {
5 const results = item_content as ProviderSearchResultsArtifact;
6 console.log("Query:", results.query);
7 results.providers.forEach((provider) => {
8 console.log(provider.name, provider.npi, provider.specialties);
9 });
10 }
11});

Scheduling progress artifact

The scheduling_progress artifact is a pinned progress indicator emitted during multi-step flows such as appointment scheduling and cancellation. The widget renders the latest emission as a progress bar at the top of the chat modal and hides the tag from the inline message bubble.

1interface SchedulingProgressArtifact {
2 current_step: number; // 1-indexed
3 total_steps: number;
4 step_label?: string; // e.g. "Insurance details"
5 flow?: string; // e.g. "schedule" or "cancel"
6 completed?: boolean; // when true, the SDK hides the progress bar
7}

Latest emission wins — the agent updates the bar by emitting a new scheduling_progress artifact, and closes the flow by emitting one with completed: true. The progress tag is consumed by the widget; if you parse messages yourself with parseEmbeddedArtifacts, filter it out before rendering.

Rendering artifacts

The createSunnyChat widget automatically parses and renders doctor profiles and provider search results from message text:

1import { createSunnyChat } from "@sunnyhealthai/agents-sdk";
2
3const chat = createSunnyChat({
4 container: document.getElementById("chat"),
5 partnerIdentifier: "acme-health",
6 publicKey: "pk-sunnyagents_abc_xyz",
7 authType: "passwordless",
8});

Doctor profiles and provider search results are automatically rendered when embedded in messages.

Embedded artifact format

Message text contains JSON blobs—no {art_tag} tags are visible to the client. The backend expands {art_tag}uuid{/art_tag} server-side before streaming. What you receive looks like:

Here's a doctor who might work for you: {"item_type":"doctor_profile","item_content":{"npi":"1234567890","first_name":"Jane","last_name":"Smith","specialty":"Cardiology","rating":4.8,"review_count":150},"created_at":"2025-01-15T12:00:00Z"}

Error handling

Malformed JSON

When parsing, handle invalid JSON gracefully:

1try {
2 const obj = JSON.parse(jsonStr);
3 if (obj?.item_type && obj?.item_content) {
4 // Valid artifact
5 }
6} catch {
7 // Skip or show fallback for malformed JSON
8}

Best practices

  1. Parse defensively: Wrap JSON.parse in try/catch; skip invalid objects
  2. Type safety: Use TypeScript assertions or guards for item_content by item_type
  3. Fallback UI: Provide fallback when an artifact fails to parse
  4. No loading states: Artifacts arrive with the message—no fetch loading needed

Next steps

  • Key Concepts - Understand events, state management, and MCP approvals
  • Authentication - Set up authenticated sessions
  • API Reference - WebSocket schemas and artifact type definitions