Guides
From your server or n8n
Give your app's own server, or an n8n workflow, a key of its own instead of the account's token. What it can do and how to call it.
Your own scripts can use the account's token. Your app's server, or an n8n workflow, should have a server key of its own: limited to one app's kinds, read or write, and revocable at once.
Make a key
Make a write server key called "Quote drafter" for the Brightwater portal, for the job and message kinds.
Claude uses awesomate_crm_apps (create_key) and shows the key (ak_...) once. Put it straight into your server's environment, never into browser code or a repository.
For n8n, ask for the key to go into an n8n credential instead (n8n_credential: true): the hub writes it into a Header Auth credential on your own n8n, usable only towards the hub, and the key never passes through anyone's hands.
Use it
const keyed = createClient({ token: process.env.AWESOMATE_APP_KEY! }); // ak_...
const { rows } = await keyed.query('job', { where: { status: 'booked' } });
await keyed.write('job', { status: 'done' }, { id: rows[0].id });
await keyed.call('book_job', { customer: contactId, title: 'Gutter clean' });From n8n or anything else, send it as Authorization: Bearer ak_... to:
| Call | Method and path |
|---|---|
| The kinds it can read | GET https://hub.awesomate.ai/api/sdk/v1/server/rows/schema |
| Query | POST https://hub.awesomate.ai/api/sdk/v1/server/rows/query |
| One record | GET https://hub.awesomate.ai/api/sdk/v1/server/rows/<kind>/<id> |
| Write, archive or a recipe | POST https://hub.awesomate.ai/api/sdk/v1/server/call |
| A saved query | POST https://hub.awesomate.ai/api/sdk/v1/server/queries/<key>/run |
What a key can and cannot do
- It reads and writes records of its own kinds, and runs saved queries and recipes that stay inside them. It reads every attribute of those kinds, and your contacts only as Claude does.
- Writing needs the Support Plus plan or above.
- It cannot change kinds, rules, apps, people, saved queries or recipes. That needs the account's token.
- It never works from a browser: any request carrying an
Originis refused. - Revoking it stops it at once, and switching the app off stops all of its keys.
Check what a token can do
Before offering something in your app, ask the hub what the account's token can reach:
const me = await db.whoami();
console.log(me.account.slug, me.plan, me.scopes);
const bookingsFeature = me.features?.bookings;
if (bookingsFeature && !bookingsFeature.available) console.log(bookingsFeature.message); // say this, and offer nothing elsekey says whose token it is: the account owner's acts as the owner; a team member's own (holder: 'person') does only what that person can do in the hub, and level says whether that is view or full. features is null when it could not be read: that means unknown, not "none". whoami() needs hosting:read; an app key cannot ask.
When the hub says no
Every refusal is an AwesomateError. Three codes say what is missing, with typed fields:
code |
Fields | What to do |
|---|---|---|
feature_unavailable |
reason, feature, upgrade |
The account does not have the feature. Show message and stop; never retry. |
upgrade_required |
requiredPlan, upgrade |
It needs a higher plan. Offer the upgrade only when the owner asks. |
scope_missing |
missingScopes |
The token lacks a scope. Make a token with it in the hub. |
try {
await db.bookings.list();
} catch (err) {
if (err instanceof AwesomateError && err.code === 'feature_unavailable') console.log(err.message, err.reason);
else if (err instanceof AwesomateError && err.code === 'scope_missing') console.log('Needs', err.missingScopes);
else throw err;
}consent_blocked means the owner switched off what the call needs in Settings (Privacy or Features). Only they can switch it back on. Every code is on Errors.