cimplify
TypeScript SDK

Support

Customer-side chat conversation. The server resolves the active conversation from the widget identity on every request; there is no `conversation_id` to thread through. Open, send, and poll.

Open (or resume) a conversation

Idempotent. Calling it twice in the same session returns the same conversation.

const r = await client.support.openConversation()
if (!r.ok) {
  console.error(r.error.code, r.error.message)
  return
}

const { conversation, starters, messages } = r.value
console.log(conversation.id, conversation.status)
console.log(starters)              // ChatWidgetStarter[] of quick-reply prompts
console.log(messages.length)       // most recent messages

Send a message

Text content plus optional attachments; the SDK adds an idempotency key so retries are safe.

const r = await client.support.sendMessage('Hi, is the cherry tee in stock?', {
  idempotencyKey: 'msg-2026-05-07-stock-check', // optional, auto-generated otherwise
})

if (!r.ok) {
  console.error(r.error.code, r.error.message)
  return
}

const message = r.value
console.log(message.id, message.created_at)
console.log(message.sender_type, message.content)

Send a file or image

Upload first, then pass the upload as an attachment. The server stores a durable reference and mints a fresh URL every time the message is read, so the media never expires.

const upload = await client.uploads.upload(file) // e.g. from an <input type="file">
if (!upload.ok) return

const r = await client.support.sendMessage('New prescription upload from the website', {
  attachments: [
    {
      upload_id: upload.value.id, // what goes on the wire
      // The rest only renders your optimistic bubble:
      url: upload.value.url,
      mime_type: upload.value.content_type,
      filename: upload.value.filename,
    },
  ],
})

Do not paste upload.value.url into the message text instead — it is a presigned link that expires after a few days and renders as a wall of URL noise in the merchant's inbox. Attachments can be images or documents (PDF, Word, Excel, CSV, plain text), up to 25 MB and 10 per message.

Poll for new messages

let cursor: string | undefined

async function poll() {
  const r = await client.support.getMessages({ after: cursor, limit: 50 })
  if (!r.ok) return

  for (const message of r.value) {
    console.log(message.sender_type, message.content)
    cursor = message.id
  }
}

const intervalId = setInterval(poll, 3000)

React: a chat widget

'use client'
import { useEffect, useState } from 'react'
import { useCimplifyClient } from '@cimplify/sdk/react'
import type { ChatMessage } from '@cimplify/sdk'

export function ChatPanel() {
  const client = useCimplifyClient()
  const [messages, setMessages] = useState<ChatMessage[]>([])
  const [input, setInput] = useState('')

  useEffect(() => {
    let active = true
    client.support.openConversation().then((r) => {
      if (active && r.ok) setMessages(r.value.messages)
    })
    return () => { active = false }
  }, [client])

  async function send() {
    const text = input.trim()
    if (!text) return
    setInput('')

    const r = await client.support.sendMessage(text)
    if (r.ok) setMessages((prev) => [...prev, r.value])
  }

  return (
    <>
      <ul>{messages.map((m) => <li key={m.id}>{m.sender_type}: {m.content}</li>)}</ul>
      <input value={input} onChange={(e) => setInput(e.target.value)} />
      <button onClick={send}>Send</button>
    </>
  )
}

getMessages options

FieldTypeNotes
afterstringLast seen message id; return only newer
limitnumberPage size

Method reference

MethodReturns
openConversation(opts?)Result<ChatConversationResponse>
sendMessage(content, opts?)opts.attachments?: { upload_id }[], opts.replyToMessageId?Result<ChatMessage>
getMessages({ after?, limit? })Result<ChatMessage[]>
  • Activity Session signals that surface contextual messages

  • Uploads Attach images / files to a chat message

  • Auth Logged-in users get conversation continuity

  • Error handling RATE_LIMITED is the most common code here

On this page