Awesomate docs v0.27.0

Reference

App client (browser)

createAppClient() and every method on it: signing in, reading and writing as the signed-in person, live lists, who is here, voice.

The client for a web page or mobile app. People sign in by email link; every call then runs as them, inside the account's access rules. Generated from the SDK's own code.

createAppClient()

The browser client for one of the account's apps (Pro and above). Give it the app's publishable key, which is not a secret; never the account's token.

createAppClient(options: AppClientOptions): AwesomateAppClient
Parameter Type
options AppClientOptions

Example

const app = createAppClient({ publishableKey: 'pk_...' });
app.auth.onChange((user) => render(user));
await app.auth.signInWithLink(email, { redirectTo: location.href });

Signing in

Email a sign-in link. The answer is the same whether or not the address can sign in.

app.auth.signInWithLink(
  email: string,
  options: { redirectTo: string },
): Promise<{ message: string; sent: boolean }>
Parameter Type
email string
options { redirectTo: string }

auth.completeSignIn()

Finish signing in from the link: the current page's address by default, or a URL a deep link opened (Capacitor). Returns the user, or null when the address carries no sign-in. The token is removed from the browser's address bar. In a browser this happens by itself (see handleSignInLinks); calling it as well is safe, and returns the same sign-in, never a second.

app.auth.completeSignIn(url?: string): Promise<AppUser | null>
Parameter Type
url (optional) string

auth.onChange()

Called with the user when someone signs in (or their details change: a new role, a linked contact) and with null on sign-out, including a refresh that was refused. A routine token refresh does not call it.

app.auth.onChange(listener: (user: AppUser | null) => void): () => void
Parameter Type
listener (user: AppUser | null) => void

auth.onSignInError()

Called when a sign-in link could not be used (expired, already used, not this app's).

app.auth.onSignInError(listener: (err: AwesomateError) => void): () => void
Parameter Type
listener (err: AwesomateError) => void

auth.user()

The signed-in user as this client last saw them (no call to the hub), or null.

app.auth.user(): Promise<AppUser | null>

auth.me()

The signed-in person read fresh from the hub (their current role and linked contact), with the app's own settings: its name, its voice agent and whether it takes uploads. A person the account has disabled is signed out, and this rejects with code unauthenticated.

app.auth.me(): Promise<AppMe>

auth.signOut()

Sign out here and end the session at the hub.

app.auth.signOut(): Promise<void>

auth.accessToken()

A current access token, for the app's own server to verify against the JWKS.

app.auth.accessToken(): Promise<string | null>

Reading

query()

Rows of one kind that this person may read, a page at a time. The database applies the kind's read rule for their role: a customer gets their own jobs, staff whatever theirs allows. A signed-in person, on an account on Pro and above.

app.query<K, S>(
  kind: K,
  options?: QueryOptions<RowOf<K>, S>,
): Promise<Omit<Page<Pick<RowOf<K>, S>>, "hidden">>
Parameter Type
kind K
options (optional) QueryOptions<RowOf<K>, S>

Example

const { rows } = await app.query('job', { orderBy: ['created_at', 'desc'], limit: 20 });

queryAll()

Every matching row this person may read, a page at a time. maxRows (default 10,000) guards against reading a whole list by accident. Pro and above.

app.queryAll<K, S>(
  kind: K,
  options?: Omit<QueryOptions<RowOf<K>, S>, "after"> & { maxRows?: number },
): AsyncGenerator<Pick<RowOf<K>, S>>
Parameter Type
kind K
options (optional) Omit<QueryOptions<RowOf<K>, S>, "after"> & { maxRows?: number }

get()

One record by id, or null when there is none this user may read. Ids are UUIDs: anything else names no record, so it answers null without asking the hub. Pro and above.

app.get<K>(kind: K, id: string): Promise<RowOf<K> | null>
Parameter Type
kind K
id string

Writing

write()

Create a record (returns its id) or change one with options.id, inside the kind's write rule for this user. Pro and above.

app.write<K>(
  kind: K,
  data: Record<string, unknown> | Partial<RowOf<K>>,
  options?: WriteOptions,
): Promise<string>
Parameter Type
kind K
data Record<string, unknown> | Partial<RowOf<K>>
options (optional) WriteOptions

archive()

Archive a record, inside the kind's write rule for this person. Pro and above.

app.archive<K>(kind: K, id: string): Promise<void>
Parameter Type
kind K
id string

call()

Run a recipe the account opened to this person's role: "accept this quote", something their write rule would not let them do by hand. Every step commits or none does; a recipe they may not run reads as not found. Never retried once sent: a write that may have landed is not safe to send twice (a refused sign-in is, and is retried after a refresh like every data call). Pro and above.

app.call<R>(recipe: R, args?: ArgsOf<R>): Promise<RecipeResult>
Parameter Type
recipe R
args (optional) ArgsOf<R>

Live

live()

A list kept current: onRows gets the whole list (up to 200 rows, the query's order) at first and again whenever a row in it is added, changed, removed, or stops being one this person may read. One connection serves every live list on this client; it reconnects by itself and takes a fresh snapshot when it does. Returns stop(). Pro and above.

app.live<K, S>(
  kind: K,
  options: Omit<QueryOptions<RowOf<K>, S>, "after">,
  handlers: LiveHandlers<Pick<RowOf<K>, S>> | ((rows: Pick<RowOf<K>, S>[], change: LiveChange<Pick<RowOf<K>, S>> | null) => void),
): () => void
Parameter Type
kind K
options Omit<QueryOptions<RowOf<K>, S>, "after">
handlers LiveHandlers<Pick<RowOf<K>, S>> | ((rows: Pick<RowOf<K>, S>[], change: LiveChange<Pick<RowOf<K>, S>> | null) => void)

here()

Open a record (a job's conversation) for who-is-here: onPeople gets everyone else who has it open with their tab in front, and who is typing, at first and on every change; the app's assistant appears while it writes a reply. The hub checks this person may read the record, and keeps checking; onError hears a refusal. Shares the one live connection. Two parts of a page (a thread and a sidebar) can open the same record: each hears onPeople, and it stays open until both have left. Returns typing() and leave(). Pro and above.

app.here(
  recordId: string,
  onPeople: (people: HerePerson[]) => void,
  onError?: (err: AwesomateError) => void,
): HereHandle
Parameter Type
recordId string
onPeople (people: HerePerson[]) => void
onError (optional) (err: AwesomateError) => void

Staff

lookupCustomers()

Customers by name or email (two characters or more), or by id, so staff can find one and put a record under them. Only for a role the account lets look customers up in this app; anyone else gets a forbidden error. Name, email and phone only, at most 50. Pro and above.

app.lookupCustomers(options: { ids?: string[]; limit?: number; q?: string }): Promise<Customer[]>
Parameter Type
options { ids?: string[]; limit?: number; q?: string }

Ask by text

ask()

Ask the app's agent a typed question, as this signed-in person: a help box that answers from the business's content, with sources. The agent is the one the account picked for the app (the same one Talk uses). Show questionNotice with the answer when it comes (a person's first). The agent answers only from content this person may see; the voice persona does not apply. A refusal's serverCode says why: no_agent, channel_off, person_daily (30 a day per person), agent_daily, answers_used_up; personMessage is the sentence to show.

app.ask(question: string, options?: { session?: string }): Promise<AgentAnswer>
Parameter Type
question string
options (optional) { session?: string }

Example

const a = await app.ask('What time do you open on Saturday?');
render(a.answer, a.sources, a.questionNotice);

Voice

voiceAgent()

The agent this app talks with by voice, or null when the account has not picked one. Pass it to AwesomateAgent.mount as agentId; the hub decides which agent answers, never the page.

app.voiceAgent(): Promise<string | null>

voiceSession()

A voice session with the app's agent, as this signed-in person: AwesomateAgent.mount({ agentId, session: () => app.voiceSession() }). The agent greets them by name and, when the app's assistant is set up, knows the recent conversations they can see, and nothing more. A refusal's serverCode says why: no_voice_agent (the app has none), plan_required, consent_required, person_daily, agent_daily; its personMessage is the sentence to show the person, which the AwesomateAgent widget shows.

app.voiceSession(): Promise<VoiceSession>

Files

files.mode()

Whether this app takes uploads: 'off', 'private' or 'public'.

app.files.mode(): Promise<"public" | "private" | "off">

files.upload()

Upload one file, up to 10 MB. The name comes from a File, or pass one. A refusal's serverCode says why: uploads_off, too_large, type_not_allowed (public uploads take no web pages or scripts), files_storage_full; personMessage is the sentence to show.

app.files.upload(file: Blob, options?: { name?: string }): Promise<AppFile>
Parameter Type
file Blob
options (optional) { name?: string }

files.download()

The file a handle names, for someone signed in to this app.

app.files.download(handle: string): Promise<Blob>
Parameter Type
handle string