Add AI to your app
Your app can use AI for its own users: draft a reply to a support ticket, summarise a long thread, give a student a hint without the answer. One call from a screen or a workflow returns text. The model key never reaches the browser, and you decide how much each app and each user may spend.
What you can build
- Suggested replies. A button on a ticket that drafts an answer the agent can edit and send.
- Summaries. A one-paragraph summary of a long record, a meeting note or a week of activity.
- Tutoring hints. A nudge in the right direction for a learner, with a system prompt that forbids giving the answer.
- Classification and triage. Tag incoming leads or tickets in a workflow before anyone looks at them.
From a screen
Every custom component gets app.ai (see the SDK). Both calls return the answer as a string.
| Call | Use it for |
|---|---|
app.ai.ask(prompt, { system? }) | One answer to one prompt |
app.ai.chat(messages, { system?, maxTokens? }) | The next assistant turn of a conversation. messages is a list of { role: "user" | "assistant", content } |
const [draft, setDraft] = useState("");
async function suggestReply() {
try {
setDraft(await app.ai.ask(`Draft a reply to this ticket:\n${ticket.body}`, {
system: "You are a friendly support agent. Answer in two sentences.",
}));
} catch {
setDraft(""); // AI is off, out of budget or offline: keep the form usable
setNote("Suggestions are unavailable right now. Write your reply below.");
}
}A tutoring chat keeps the conversation and sends it each turn:
const next = await app.ai.chat(
[...history, { role: "user", content: question }],
{
system: "You are a maths tutor. Give one hint at a time. Never give the final answer.",
maxTokens: 300,
},
);
setHistory([...history, { role: "user", content: question }, { role: "assistant", content: next }]);Always have a fallback
The calls reject with a message when AI is not available: AI is turned off for the project, a budget or rate limit is reached, there are no credits, or the preview is offline. Put AI behind a button, catch the error, and let the user carry on without it. Never make a core action depend on an AI answer.
From a workflow
Workflows get the same two calls on ctx.ai, with the same signatures. Use them to classify, summarise or draft on the server, after a change or on a schedule.
export async function run(ctx) {
const ticket = ctx.trigger.payload;
let priority = "normal";
try {
const answer = await ctx.ai.ask(`Ticket: ${ticket.body}`, {
system: "Reply with exactly one word: urgent, normal or low.",
});
if (["urgent", "normal", "low"].includes(answer.trim())) priority = answer.trim();
} catch (err) {
ctx.log.warn("AI unavailable, using default priority", { error: String(err) });
}
await ctx.api.update("ticket", ticket.id, { priority });
}Stay in control of cost
Each project has its own AI settings, so one busy app cannot spend another's budget:
- On or off. Turn AI off for a project and every call rejects straight away, with no model usage.
- Model allow-list. Limit which models the app may call.
- Token budgets.A budget for the whole app and a budget per user, over a window you choose (30 days by default). A single user cannot drain the app's budget.
- Rate limits. Requests per project are rate limited, so a loop or an abusive client is cut off quickly.
- Usage. Project Settings → Appbricx AI shows the tokens, requests and cost your app has used.
Which key pays for it
- Your own model key. Add it under AI settings, then pin it to the project in Project Settings → Appbricx AI. Your provider bills you directly, at cost.
- Otherwise, plan credits.With no key pinned, calls use your plan's AI credits.
Either way the key stays on the server. The browser only ever sees the answer. For the full picture see Which AI key is used and Use your own AI key.
Related: Frontend SDK · Workflows · Your screens and brand