DEVELOPER GUIDE

AI Model Trainer API

Connect the models you train to the tools you use.

Classify from your own tools

Call the models you train in StayCharted AI Model Trainer from your own code, a spreadsheet, or an automation tool.

Base URL https://amt.staycharted.com

You train a model in the app by uploading examples you've already labelled. Then send it new text, or links to pictures, and get back a category and a confidence. Three endpoints cover everything:

Endpoint Use it for
GET /api/v1/models Listing the models you can call, and testing a key
POST /api/v1/models/{id}/classify One record in, one flat answer out. Best for Zapier, Make, Power Automate, Salesforce and spreadsheets
POST /api/v1/models/{id}/predict One record, or up to 100 at once. Best for your own code

The API is included on Essentials and Business. See Pricing.

Get started in three steps

1. Create a key. Sign in to AI Model Trainer, open Settings → API keys, and create one.

  • The full key (it starts with amt_live_) is shown once. Store it then.
  • Up to ten keys can be active at once. Give each integration its own, so you can revoke one without breaking the others.

2. Find your model's id. It's listed on the same screen. Or call GET /api/v1/models, which also confirms the key works and costs nothing. Only published models can be called.

3. Classify something.

curl -sS https://amt.staycharted.com/api/v1/models/$MODEL_ID/classify \
  -H "Authorization: Bearer $STAYCHARTED_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"input":"My invoice charged me twice this month"}'
{
  "label": "Billing",
  "confidence": 0.94,
  "modelId": "8f14e45f-ceea-467a-9b2c-1d5c2b0e7a11",
  "modelName": "Support tickets"
}

label is the answer. confidence runs from 0 to 1.

What it costs, and the one limit to design around

Every record is one prediction, drawn from the same monthly allowance your file fills in the app use. A request that is refused, for any reason below, costs nothing.

The rate limit is counted in records per minute, for the whole workspace: 30 a minute on Essentials, 60 on Business.

  • Every key draws on that one allowance, so adding keys doesn't add throughput.
  • The allowance refills continuously; it doesn't reset on the minute.
  • A request that doesn't fit is refused whole with 429 and a Retry-After header, and nothing is charged.

So the API is for a steady trickle, not a flood. It suits a ticket as it arrives, a row as it's created, a form as it's submitted. For thousands of rows, upload the file in the app instead. It runs in one pass on hardware sized for the job, and you download the filled file.

Recipes

Google Sheets: =CLASSIFY(A2)

In your sheet: Extensions → Apps Script, paste this, save, and go back to the sheet.

const STAYCHARTED_KEY   = 'amt_live_...';  // your API key
const STAYCHARTED_MODEL = '8f14e45f-...';  // your model id

/**
 * Classify one cell.
 * @param {string} text The cell to classify.
 * @return {string} The label.
 * @customfunction
 */
function CLASSIFY(text) {
  if (!text || !String(text).trim()) return '';

  const url = 'https://amt.staycharted.com/api/v1/models/'
    + STAYCHARTED_MODEL + '/classify';

  // Two attempts: a column of formulas recalculates all at once and the
  // per-minute allowance is shared. Retry-After says exactly how long to wait.
  for (let attempt = 0; attempt < 2; attempt++) {
    const res = UrlFetchApp.fetch(url, {
      method: 'post',
      contentType: 'application/json',
      headers: { Authorization: 'Bearer ' + STAYCHARTED_KEY },
      payload: JSON.stringify({ input: String(text) }),
      muteHttpExceptions: true,
    });

    const code = res.getResponseCode();
    if (code === 200) return JSON.parse(res.getContentText()).label || '';

    if (code === 429 && attempt === 0) {
      const wait = Number(res.getAllHeaders()['Retry-After'] || 5);
      Utilities.sleep(Math.min(wait, 25) * 1000);
      continue;
    }
    // Show the reason rather than an empty cell.
    return 'ERR ' + code + ': ' + (JSON.parse(res.getContentText()).code || '');
  }
  return 'ERR rate limited';
}

Then type =CLASSIFY(A2) in a cell and drag it down.

  • Start with a few rows at a time. Sheets recalculates the whole column at once. Large selections can exceed the shared rate limit and return ERR rate limited. For more than a few hundred rows, fill the file in the app instead.
  • The key travels with the sheet. Anyone you share the sheet with can read the script. Give it its own key, and revoke the key when you're done.

Zapier

Add a Webhooks by Zapier → Custom Request action.

Field Value
Method POST
URL https://amt.staycharted.com/api/v1/models/<MODEL_ID>/classify
Data Pass-Through? no
Data {"input": "<the field from your trigger>"}
Headers Authorization: Bearer <YOUR_API_KEY> and Content-Type: application/json

The next step can use label, confidence, modelId and modelName as plain fields. Zapier counts one task per action, so a thousand records is a thousand tasks.

Make

Add HTTP → Make a request.

  • URL: https://amt.staycharted.com/api/v1/models/<MODEL_ID>/classify
  • Method: POST
  • Headers: Authorization = Bearer <YOUR_API_KEY>
  • Body type: Raw, content type application/json
  • Request content: {"input": "{{yourField}}"}
  • Parse response: yes. Without it, the next module sees a string instead of label.

Your own code: /predict for batches

Send up to 100 records in one call. Answers come back in the order sent, each with its input echoed.

curl -sS https://amt.staycharted.com/api/v1/models/$MODEL_ID/predict \
  -H "Authorization: Bearer $STAYCHARTED_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"inputs":["My invoice charged me twice this month","The parcel arrived with a cracked screen"]}'
{
  "model": { "id": "8f14e45f-ceea-467a-9b2c-1d5c2b0e7a11", "name": "Support tickets" },
  "predictions": [
    { "input": "My invoice charged me twice this month", "label": "Billing", "confidence": 0.94 },
    { "input": "The parcel arrived with a cracked screen", "label": "Shipping", "confidence": 0.71 }
  ],
  "usage": { "predictions": 2 }
}

The whole request is checked against your remaining monthly allowance before any of it runs. A request that would go over is refused entirely, never half-answered.

AI assistants

To use your models from Claude or ChatGPT instead of code, connect the assistant instead. See Use AI Model Trainer from Claude and ChatGPT.

Reference

Authentication

Send your key in every request:

Authorization: Bearer amt_live_…

Keys are created and revoked only in the app, never through the API. A key that could create keys couldn't really be revoked.

GET /api/v1/models

Lists the models this key can call. A model appears only when it is published, switched on, and allowed by your plan. This call spends no predictions and has its own allowance of 60 requests a minute.

{
  "models": [
    { "id": "8f14e45f-ceea-467a-9b2c-1d5c2b0e7a11", "name": "Support tickets", "custom": true },
    { "id": "3c59dc04-8e88-4950-9f2b-7a1c2a1e0b02", "name": "Product feedback", "custom": false }
  ]
}

custom is true for a Dedicated AI Model. It is false for a Classifier Model or an AI Classifier Model. All of them are called the same way.

POST /api/v1/models/{id}/classify

One record in, one flat answer out.

Request field Type Notes
input string, or object Text model: the text. Picture model: a link to the picture (http or https), or {"imageBase64": "..."} for a picture under 1.5 MB.
Response field Type Notes
label string or null The category.
confidence number or null 0 to 1.
modelId string
modelName string
error string Picture models only, when the picture couldn't be used. See below.
message string The same reason in plain words.

POST /api/v1/models/{id}/predict

Exactly one of:

Request field Type Notes
input string One record.
inputs array 1 to 100 records. For a picture model: up to 20 pictures, each a link or {"imageBase64": "..."}.
Response field Type Notes
model.id, model.name string
predictions[] array One per input, in order: input, label, confidence, and for a picture that couldn't be used, error and message.
usage.predictions integer Records counted against this month's allowance.

predictions is always an array, even for a single input.

Pictures

For a model that reads pictures:

  • Each picture counts once against your monthly picture allowance, as well as once as a prediction.
  • A picture that can't be used comes back with label and confidence set to null, plus an error code and a message, and isn't counted. The codes are: not_found, forbidden, unreachable, timeout, too_large, web_page, not_an_image, unsupported_format, too_small, corrupt, blocked_address, not_a_link.
  • Accepted formats: JPEG, PNG, WebP, GIF, BMP and TIFF.

Rate-limit headers

Header Meaning
X-RateLimit-Limit Records a minute your plan allows, for the whole workspace.
X-RateLimit-Remaining Records you can send right now.
Retry-After On a 429: seconds until this request will fit. A smaller request may fit sooner.

Errors

Every error is JSON with a stable code. Branch on code, never on the error sentence, which is written for people and may be reworded.

A refusal caused by your plan also carries entitlement, limit, used, resetsAt and upgrade. Those let a client tell "wait until the month resets" from "this plan will never allow it".

HTTP code What it means, and what to do
400 api.input_missing classify got no usable input. It takes one string; an array is /predict's shape.
400 api.input_required /predict needs input (one string) or inputs (an array).
400 api.input_invalid Every input must be a non-empty string.
400 api.too_many_inputs More than 100 records in one request. Send several requests, or fill the file in the app.
400 api.too_many_images More than 20 pictures in one request.
400 api.text_input_invalid This model reads text: send each input as a string.
400 api.image_input_invalid This model reads pictures: send a link or {"imageBase64": "..."}.
400 api.image_too_large A picture sent in the request is over 1.5 MB. Send a link instead.
401 api.key_missing No Authorization: Bearer <key> header.
401 api.key_invalid The key is wrong or revoked. Create a new one.
402 plan.feature_not_included The API isn't on this plan. Retrying won't help; upgrade names the plan that includes it.
402 plan.prediction_limit This month's predictions are used up. resetsAt says when they come back.
403 account.suspended Contact support.
404 api.model_not_found Wrong model id, or the model is in another workspace.
409 api.model_not_deployed The model is trained but not published. Publish it in the app.
409 api.model_needs_retraining The model must be retrained before it can answer again. Its owner retrains it in the app.
429 api.rate_limited Over the per-minute allowance. Wait Retry-After seconds.
503 api.model_unavailable A service the model needs is briefly unavailable, usually during a restart. Retry after a short wait. Nothing was charged.

Need help connecting?

Contact support@staycharted.com.

Get an API key →