StayCharted AI Model TrainerPartner APIPrediction APIstaycharted.com
Contents

Partner

GETWho this key belongs to

Customers

GETYour customersPOSTAdd a customer (charged first)GETOne customerPATCHRename, or change the contact emailPOSTChange a workspace's plan or add-onsPOSTAdd another paid workspacePOSTStop paying for a workspacePOSTHand the customer over (it goes direct)POSTCancel the customerPOSTSend a resold customer's set-up link againGETDownload the customer's data

Models

GETThe customer's modelsPOSTCreate a modelGETOne model and its versionsPATCHRename or describe a modelDELETEDelete a modelPOSTWhich columns say something about the answerPOSTSend the training dataPOSTChoose the columns, after sending a fileGETThe data-quality findingsPOSTMerge categories, leave some out, or remove duplicatesPOSTStart trainingGETTraining progress, then the reportPOSTMake a trained version live

Predictions

POSTAsk the published model, one record or manyPOSTAsk the published model one record, flat answerPOSTStart a batch fillGETFill progress, then the answersGETDownload the filled file

Review Queue

GETAnswers waiting for a personPOSTAnswer onePOSTRetrain with the confirmed answers

Reference

AuthenticationError codesRate limitingSchemas

StayCharted AI Model Trainer Partner API

Version 1.0.0 · Base URL https://amt.staycharted.com · OpenAPI 3.0

For solution partners: software companies that build StayCharted AI Model Trainer (AMT) into their own product under a Solution Partner Agreement. One partner key reaches every customer the partner manages, and nothing else.

What a partner can do by key

Two kinds of customer. access: api (the default) is a customer that uses AMT only through your product: no login, and your key does everything. access: login is a resold customer whose own people sign in to AMT: your key manages its plan and workspaces, and never its models or data (partner.customer_signs_in).

The key. Authorization: Bearer amt_partner_…. StayCharted issues it; your account Owner receives a one-time link and sees it once. Up to two keys at a time, so you can replace one without downtime; either can be limited to IP addresses and revoked at once from your Partner page. Keep it in a secrets manager, on your servers only.

Limits. 600 calls a minute per key, and 30 new customers a minute per partner, then 429 with Retry-After. Each customer also keeps its own plan's allowances and rate limit, exactly as a direct customer has.

Errors. JSON with a stable code. Branch on code, never on the error text, which is written for people and may be reworded. A customer, model or job that is not yours answers 404, the same as one that does not exist.

Changes. Additive changes (a new field, a new endpoint) can come at any time, so ignore fields you do not know. A breaking change to a documented endpoint or field comes with 60 days' notice, as the Solution Partner Agreement says.

Activity. Everything done by key appears in the customer's Activity as "via <partner> (Partner API: <key name>)", and every prediction records it.

Questions: partners@staycharted.com.

Authentication

Authorization: Bearer amt_partner_…. Issued by StayCharted; up to two live at once.

GET /api/v1/partner HTTP/1.1
Host: amt.staycharted.com
Authorization: Bearer amt_partner_…

Endpoints

Partner

Customers

Models

Predictions

Review Queue

Partner

Who the key belongs to.

GET /api/v1/partner

Who this key belongs to

Cheap, and touches no customer, so it is the call to check a key with.

Responses

200 — The partner and this key.

FieldTypeRequiredNotes
partnerobjectno
partner.idstring (uuid)no
partner.namestringno
partner.discountPercentnumber | nullno
keyobjectno
key.namestringno
key.prefixstringnoThe key's first characters, to tell your two keys apart.
customersintegernoHow many customers you manage.
billingobjectno
billing.statusstring | nullno
billing.paidUntilstring (date-time) | nullno
{
  "partner": {
    "id": "5b1f0c3e-1a2b-4c3d-8e9f-0a1b2c3d4e5f",
    "name": "Acme Helpdesk",
    "discountPercent": 20
  },
  "key": {
    "name": "Production",
    "prefix": "amt_partner_7Qk2"
  },
  "customers": 37,
  "billing": {
    "status": "active",
    "paidUntil": "2026-11-26T00:00:00.000Z"
  }
}
Errors: 401, 403, 429

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

Customers

Add, change, hand over and cancel the customers you manage.

GET /api/v1/customers

Your customers

Responses

200 — Every customer you manage, with this month's usage (counts only, never content).

FieldTypeRequiredNotes
customersarray of objectyes
customers[].idstring (uuid)yes
customers[].namestringyes
customers[].contactEmailstringno
customers[].accessstringyes(one of api, login)
customers[].joinedFromDirectbooleannoThe customer already paid StayCharted itself and asked to be billed through you. Its account was its own before you, so cancelling it moves it back to direct billing rather than closing it.
customers[].statusstringyes(one of active, cancelling, cancelled, handed_over)
customers[].planstring | nullnoThe first workspace's plan.
customers[].pendingPlanstring | nullno
customers[].addOnsarray of stringno
customers[].endsAtstring (date-time) | nullno
customers[].createdAtstring (date-time)no
customers[].usageobject | nullnoThis billing month's counts. Never content.
customers[].usage.cycleStartstring (date-time) | nullno
customers[].usage.predictionsintegerno
customers[].usage.predictionsLimitinteger | nullno
customers[].usage.picturesintegerno
customers[].usage.picturesLimitinteger | nullno
customers[].workspacesarray of objectno
customers[].workspaces[].idstring (uuid)no
customers[].workspaces[].namestringno
customers[].workspaces[].planstringno
customers[].workspaces[].pendingPlanstring | nullnoA downgrade waiting for the end of the paid month.
customers[].workspaces[].changesAtstring (date-time) | nullno
customers[].workspaces[].endsAtstring (date-time) | nullnoSet when you stopped paying for it.
customers[].workspaces[].archivedbooleanno
customers[].workspaces[].primarybooleannoThe customer's first workspace.
customers[].workspaces[].joinsAtstring (date-time) | nullnoFor a customer that joined you from its own billing: when this workspace moves to your bill, at the end of the month its Owner already paid for. Until then it is not on your bill and its plan cannot be changed (partner.workspace_joining).
customers[].workspaces[].addOnsarray of stringno
customers[].workspaces[].usageobject | nullnoThis billing month's counts. Never content.
customers[].workspaces[].usage.cycleStartstring (date-time) | nullno
customers[].workspaces[].usage.predictionsintegerno
customers[].workspaces[].usage.predictionsLimitinteger | nullno
customers[].workspaces[].usage.picturesintegerno
customers[].workspaces[].usage.picturesLimitinteger | nullno
customers[].ownerSignedInAtstring (date-time) | nullnoA resold customer only. When its Owner last signed in; null until they have.
Errors: 401, 403, 429

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

POST /api/v1/customers

Add a customer (charged first)

Charges your card for the rest of this month on the chosen plan and add-ons, at your discount, and only then creates the customer's account and its first workspace. A declined card creates nothing (402).

For access: api (the default) the plan must include the API: Essentials or Business. With login: true the customer is resold: its contact becomes the account's Owner and gets a link to set a password (or, if the address already signs in to StayCharted, the account appears in their workspace switcher).

Request body (application/json)

FieldTypeRequiredNotes
namestringyes
planstringyes(one of essentials, business)
addOnsarray of stringnoAdd-on ids. vision reads pictures; byom is Your AI Models. What a plan offers is on the pricing page.
contactEmailstring (email)yesThe customer's contact. For access: api, the address that gets a login link if the customer is ever handed over; nothing is sent before then. For a resold customer (login: true), the account's Owner, mailed at once.
loginbooleannoTrue for a resold customer whose own people sign in to AMT.
{
  "name": "Northwind Traders",
  "plan": "essentials",
  "addOns": [
    "vision"
  ],
  "contactEmail": "it@northwind.example"
}

Responses

201 — Paid for and created.

FieldTypeRequiredNotes
idstring (uuid)yes
namestringyes
contactEmailstringno
accessstringyes(one of api, login)
joinedFromDirectbooleannoThe customer already paid StayCharted itself and asked to be billed through you. Its account was its own before you, so cancelling it moves it back to direct billing rather than closing it.
statusstringyes(one of active, cancelling, cancelled, handed_over)
planstring | nullnoThe first workspace's plan.
pendingPlanstring | nullno
addOnsarray of stringno
endsAtstring (date-time) | nullno
createdAtstring (date-time)no
usageobject | nullnoThis billing month's counts. Never content.
usage.cycleStartstring (date-time) | nullno
usage.predictionsintegerno
usage.predictionsLimitinteger | nullno
usage.picturesintegerno
usage.picturesLimitinteger | nullno
workspacesarray of objectno
workspaces[].idstring (uuid)no
workspaces[].namestringno
workspaces[].planstringno
workspaces[].pendingPlanstring | nullnoA downgrade waiting for the end of the paid month.
workspaces[].changesAtstring (date-time) | nullno
workspaces[].endsAtstring (date-time) | nullnoSet when you stopped paying for it.
workspaces[].archivedbooleanno
workspaces[].primarybooleannoThe customer's first workspace.
workspaces[].joinsAtstring (date-time) | nullnoFor a customer that joined you from its own billing: when this workspace moves to your bill, at the end of the month its Owner already paid for. Until then it is not on your bill and its plan cannot be changed (partner.workspace_joining).
workspaces[].addOnsarray of stringno
workspaces[].usageobject | nullnoThis billing month's counts. Never content.
workspaces[].usage.cycleStartstring (date-time) | nullno
workspaces[].usage.predictionsintegerno
workspaces[].usage.predictionsLimitinteger | nullno
workspaces[].usage.picturesintegerno
workspaces[].usage.picturesLimitinteger | nullno
ownerSignedInAtstring (date-time) | nullnoA resold customer only. When its Owner last signed in; null until they have.
Errors: 400, 402, 409, 401, 403, 429, 502

400 — Something in the request is missing or not offered.

Example — name

{
  "error": "Give the customer a name.",
  "code": "partner.customer_name"
}

Example — contact

{
  "error": "Give the customer contact’s email address.",
  "code": "partner.contact_email"
}

Example — plan

{
  "error": "Choose a plan for the customer: \"essentials\" or \"business\".",
  "code": "partner.plan"
}

Example — plan_no_api

{
  "error": "That plan does not include the API, so a partner's product cannot use it.",
  "code": "partner.plan_no_api"
}

Example — addon

{
  "error": "The faces add-on cannot be added: there is no such add-on.",
  "code": "partner.addon"
}

402 — Your card was not charged, so nothing was created or changed.

Example — declined

{
  "error": "Your card was not charged, so nothing was created or changed. Update the card on your Partner page in StayCharted, then try again.",
  "code": "partner.payment_declined"
}

Example — no_card

{
  "error": "There is no card on your partner account yet. Add one on your Partner page in StayCharted, then try again.",
  "code": "partner.card_required"
}

409 — Another change to your bill is going through. Retry in a minute.

{
  "error": "Another change to this partner’s bill is still going through. Try again in a minute.",
  "code": "partner.busy"
}

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

502 — The payment processor could not be reached or refused the change for another reason. Nothing was changed; retry later.

{
  "error": "Stripe refused the change: …",
  "code": "partner.billing_unavailable"
}

GET /api/v1/customers/{customerId}

One customer

Path parameters

NameTypeRequiredDescription
customerIdstring (uuid)yesThe customer's id, from GET /api/v1/customers.

Responses

200 — The customer.

FieldTypeRequiredNotes
idstring (uuid)yes
namestringyes
contactEmailstringno
accessstringyes(one of api, login)
joinedFromDirectbooleannoThe customer already paid StayCharted itself and asked to be billed through you. Its account was its own before you, so cancelling it moves it back to direct billing rather than closing it.
statusstringyes(one of active, cancelling, cancelled, handed_over)
planstring | nullnoThe first workspace's plan.
pendingPlanstring | nullno
addOnsarray of stringno
endsAtstring (date-time) | nullno
createdAtstring (date-time)no
usageobject | nullnoThis billing month's counts. Never content.
usage.cycleStartstring (date-time) | nullno
usage.predictionsintegerno
usage.predictionsLimitinteger | nullno
usage.picturesintegerno
usage.picturesLimitinteger | nullno
workspacesarray of objectno
workspaces[].idstring (uuid)no
workspaces[].namestringno
workspaces[].planstringno
workspaces[].pendingPlanstring | nullnoA downgrade waiting for the end of the paid month.
workspaces[].changesAtstring (date-time) | nullno
workspaces[].endsAtstring (date-time) | nullnoSet when you stopped paying for it.
workspaces[].archivedbooleanno
workspaces[].primarybooleannoThe customer's first workspace.
workspaces[].joinsAtstring (date-time) | nullnoFor a customer that joined you from its own billing: when this workspace moves to your bill, at the end of the month its Owner already paid for. Until then it is not on your bill and its plan cannot be changed (partner.workspace_joining).
workspaces[].addOnsarray of stringno
workspaces[].usageobject | nullnoThis billing month's counts. Never content.
workspaces[].usage.cycleStartstring (date-time) | nullno
workspaces[].usage.predictionsintegerno
workspaces[].usage.predictionsLimitinteger | nullno
workspaces[].usage.picturesintegerno
workspaces[].usage.picturesLimitinteger | nullno
ownerSignedInAtstring (date-time) | nullnoA resold customer only. When its Owner last signed in; null until they have.
Errors: 401, 403, 404, 429

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

404 — No customer of yours with that id.

{
  "error": "No such customer of this partner.",
  "code": "partner.customer_not_found"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

PATCH /api/v1/customers/{customerId}

Rename, or change the contact email

A resold customer names its own account (409 partner.customer_names_itself); its contact email can still change.

Path parameters

NameTypeRequiredDescription
customerIdstring (uuid)yesThe customer's id, from GET /api/v1/customers.

Request body (application/json)

FieldTypeRequiredNotes
namestringno
contactEmailstring (email)no

Responses

200 — The customer, changed.

FieldTypeRequiredNotes
idstring (uuid)yes
namestringyes
contactEmailstringno
accessstringyes(one of api, login)
joinedFromDirectbooleannoThe customer already paid StayCharted itself and asked to be billed through you. Its account was its own before you, so cancelling it moves it back to direct billing rather than closing it.
statusstringyes(one of active, cancelling, cancelled, handed_over)
planstring | nullnoThe first workspace's plan.
pendingPlanstring | nullno
addOnsarray of stringno
endsAtstring (date-time) | nullno
createdAtstring (date-time)no
usageobject | nullnoThis billing month's counts. Never content.
usage.cycleStartstring (date-time) | nullno
usage.predictionsintegerno
usage.predictionsLimitinteger | nullno
usage.picturesintegerno
usage.picturesLimitinteger | nullno
workspacesarray of objectno
workspaces[].idstring (uuid)no
workspaces[].namestringno
workspaces[].planstringno
workspaces[].pendingPlanstring | nullnoA downgrade waiting for the end of the paid month.
workspaces[].changesAtstring (date-time) | nullno
workspaces[].endsAtstring (date-time) | nullnoSet when you stopped paying for it.
workspaces[].archivedbooleanno
workspaces[].primarybooleannoThe customer's first workspace.
workspaces[].joinsAtstring (date-time) | nullnoFor a customer that joined you from its own billing: when this workspace moves to your bill, at the end of the month its Owner already paid for. Until then it is not on your bill and its plan cannot be changed (partner.workspace_joining).
workspaces[].addOnsarray of stringno
workspaces[].usageobject | nullnoThis billing month's counts. Never content.
workspaces[].usage.cycleStartstring (date-time) | nullno
workspaces[].usage.predictionsintegerno
workspaces[].usage.predictionsLimitinteger | nullno
workspaces[].usage.picturesintegerno
workspaces[].usage.picturesLimitinteger | nullno
ownerSignedInAtstring (date-time) | nullnoA resold customer only. When its Owner last signed in; null until they have.
Errors: 400, 409, 401, 403, 404, 429

400 — An empty name or an address that is not one.

{
  "error": "That is not an email address.",
  "code": "partner.contact_email"
}

409 — A resold customer names its own account.

{
  "error": "This customer signs in to StayCharted and names its own account.",
  "code": "partner.customer_names_itself"
}

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

404 — No customer of yours with that id.

{
  "error": "No such customer of this partner.",
  "code": "partner.customer_not_found"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

POST /api/v1/customers/{customerId}/plan

Change a workspace's plan or add-ons

An upgrade is charged at once and takes effect at once; a downgrade takes effect at the end of the paid month (pendingPlan and changesAt say when). Without workspaceId, the customer's first workspace.

Path parameters

NameTypeRequiredDescription
customerIdstring (uuid)yesThe customer's id, from GET /api/v1/customers.

Request body (application/json)

FieldTypeRequiredNotes
planstringyes(one of essentials, business)
addOnsarray of stringnoAdd-on ids. vision reads pictures; byom is Your AI Models. What a plan offers is on the pricing page.
workspaceIdstring (uuid)no
{
  "plan": "business",
  "addOns": []
}

Responses

200 — The customer, with the change made or scheduled.

FieldTypeRequiredNotes
idstring (uuid)yes
namestringyes
contactEmailstringno
accessstringyes(one of api, login)
joinedFromDirectbooleannoThe customer already paid StayCharted itself and asked to be billed through you. Its account was its own before you, so cancelling it moves it back to direct billing rather than closing it.
statusstringyes(one of active, cancelling, cancelled, handed_over)
planstring | nullnoThe first workspace's plan.
pendingPlanstring | nullno
addOnsarray of stringno
endsAtstring (date-time) | nullno
createdAtstring (date-time)no
usageobject | nullnoThis billing month's counts. Never content.
usage.cycleStartstring (date-time) | nullno
usage.predictionsintegerno
usage.predictionsLimitinteger | nullno
usage.picturesintegerno
usage.picturesLimitinteger | nullno
workspacesarray of objectno
workspaces[].idstring (uuid)no
workspaces[].namestringno
workspaces[].planstringno
workspaces[].pendingPlanstring | nullnoA downgrade waiting for the end of the paid month.
workspaces[].changesAtstring (date-time) | nullno
workspaces[].endsAtstring (date-time) | nullnoSet when you stopped paying for it.
workspaces[].archivedbooleanno
workspaces[].primarybooleannoThe customer's first workspace.
workspaces[].joinsAtstring (date-time) | nullnoFor a customer that joined you from its own billing: when this workspace moves to your bill, at the end of the month its Owner already paid for. Until then it is not on your bill and its plan cannot be changed (partner.workspace_joining).
workspaces[].addOnsarray of stringno
workspaces[].usageobject | nullnoThis billing month's counts. Never content.
workspaces[].usage.cycleStartstring (date-time) | nullno
workspaces[].usage.predictionsintegerno
workspaces[].usage.predictionsLimitinteger | nullno
workspaces[].usage.picturesintegerno
workspaces[].usage.picturesLimitinteger | nullno
ownerSignedInAtstring (date-time) | nullnoA resold customer only. When its Owner last signed in; null until they have.
Errors: 400, 402, 404, 409, 401, 403, 429, 502

400 — The plan or an add-on is not offered.

{
  "error": "Choose a plan for the customer: \"essentials\" or \"business\".",
  "code": "partner.plan"
}

402 — Your card was not charged, so nothing was created or changed.

Example — declined

{
  "error": "Your card was not charged, so nothing was created or changed. Update the card on your Partner page in StayCharted, then try again.",
  "code": "partner.payment_declined"
}

Example — no_card

{
  "error": "There is no card on your partner account yet. Add one on your Partner page in StayCharted, then try again.",
  "code": "partner.card_required"
}

404 — No such customer, or no such workspace of it.

{
  "error": "No such workspace for this customer.",
  "code": "partner.workspace_not_found"
}

409 — The customer or the workspace is already ending, or another change to your bill is going through.

Example — customer_ending

{
  "error": "That customer is being cancelled, so its plan cannot change.",
  "code": "partner.customer_ending"
}

Example — workspace_ending

{
  "error": "That workspace is already ending at the end of the paid month.",
  "code": "partner.workspace_ending"
}

Example — busy

{
  "error": "Another change to this partner’s bill is still going through. Try again in a minute.",
  "code": "partner.busy"
}

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

502 — The payment processor could not be reached or refused the change for another reason. Nothing was changed; retry later.

{
  "error": "Stripe refused the change: …",
  "code": "partner.billing_unavailable"
}

POST /api/v1/customers/{customerId}/workspaces

Add another paid workspace

Charged first, like adding a customer. The name must be new for this customer.

Path parameters

NameTypeRequiredDescription
customerIdstring (uuid)yesThe customer's id, from GET /api/v1/customers.

Request body (application/json)

FieldTypeRequiredNotes
namestringyes
planstringyes(one of essentials, business)
addOnsarray of stringnoAdd-on ids. vision reads pictures; byom is Your AI Models. What a plan offers is on the pricing page.
{
  "name": "Finance",
  "plan": "essentials"
}

Responses

201 — The customer, with the new workspace.

FieldTypeRequiredNotes
idstring (uuid)yes
namestringyes
contactEmailstringno
accessstringyes(one of api, login)
joinedFromDirectbooleannoThe customer already paid StayCharted itself and asked to be billed through you. Its account was its own before you, so cancelling it moves it back to direct billing rather than closing it.
statusstringyes(one of active, cancelling, cancelled, handed_over)
planstring | nullnoThe first workspace's plan.
pendingPlanstring | nullno
addOnsarray of stringno
endsAtstring (date-time) | nullno
createdAtstring (date-time)no
usageobject | nullnoThis billing month's counts. Never content.
usage.cycleStartstring (date-time) | nullno
usage.predictionsintegerno
usage.predictionsLimitinteger | nullno
usage.picturesintegerno
usage.picturesLimitinteger | nullno
workspacesarray of objectno
workspaces[].idstring (uuid)no
workspaces[].namestringno
workspaces[].planstringno
workspaces[].pendingPlanstring | nullnoA downgrade waiting for the end of the paid month.
workspaces[].changesAtstring (date-time) | nullno
workspaces[].endsAtstring (date-time) | nullnoSet when you stopped paying for it.
workspaces[].archivedbooleanno
workspaces[].primarybooleannoThe customer's first workspace.
workspaces[].joinsAtstring (date-time) | nullnoFor a customer that joined you from its own billing: when this workspace moves to your bill, at the end of the month its Owner already paid for. Until then it is not on your bill and its plan cannot be changed (partner.workspace_joining).
workspaces[].addOnsarray of stringno
workspaces[].usageobject | nullnoThis billing month's counts. Never content.
workspaces[].usage.cycleStartstring (date-time) | nullno
workspaces[].usage.predictionsintegerno
workspaces[].usage.predictionsLimitinteger | nullno
workspaces[].usage.picturesintegerno
workspaces[].usage.picturesLimitinteger | nullno
ownerSignedInAtstring (date-time) | nullnoA resold customer only. When its Owner last signed in; null until they have.
Errors: 400, 402, 409, 401, 403, 404, 429, 502

400 — No name, or a plan or add-on that is not offered.

{
  "error": "Give the workspace a name.",
  "code": "partner.workspace_name"
}

402 — Your card was not charged, so nothing was created or changed.

Example — declined

{
  "error": "Your card was not charged, so nothing was created or changed. Update the card on your Partner page in StayCharted, then try again.",
  "code": "partner.payment_declined"
}

Example — no_card

{
  "error": "There is no card on your partner account yet. Add one on your Partner page in StayCharted, then try again.",
  "code": "partner.card_required"
}

409 — The name is taken, the customer is ending, or another change to your bill is going through.

{
  "error": "This customer already has a workspace called \"Finance\".",
  "code": "partner.workspace_name_taken"
}

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

404 — No customer of yours with that id.

{
  "error": "No such customer of this partner.",
  "code": "partner.customer_not_found"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

502 — The payment processor could not be reached or refused the change for another reason. Nothing was changed; retry later.

{
  "error": "Stripe refused the change: …",
  "code": "partner.billing_unavailable"
}

POST /api/v1/customers/{customerId}/workspaces/{workspaceId}/remove

Stop paying for a workspace

The workspace runs to the end of the paid month, then is archived. The customer's first workspace cannot be removed this way: cancel the customer instead (partner.workspace_primary).

Path parameters

NameTypeRequiredDescription
customerIdstring (uuid)yesThe customer's id, from GET /api/v1/customers.
workspaceIdstring (uuid)yes

Responses

200 — The customer, with the workspace's endsAt set.

FieldTypeRequiredNotes
idstring (uuid)yes
namestringyes
contactEmailstringno
accessstringyes(one of api, login)
joinedFromDirectbooleannoThe customer already paid StayCharted itself and asked to be billed through you. Its account was its own before you, so cancelling it moves it back to direct billing rather than closing it.
statusstringyes(one of active, cancelling, cancelled, handed_over)
planstring | nullnoThe first workspace's plan.
pendingPlanstring | nullno
addOnsarray of stringno
endsAtstring (date-time) | nullno
createdAtstring (date-time)no
usageobject | nullnoThis billing month's counts. Never content.
usage.cycleStartstring (date-time) | nullno
usage.predictionsintegerno
usage.predictionsLimitinteger | nullno
usage.picturesintegerno
usage.picturesLimitinteger | nullno
workspacesarray of objectno
workspaces[].idstring (uuid)no
workspaces[].namestringno
workspaces[].planstringno
workspaces[].pendingPlanstring | nullnoA downgrade waiting for the end of the paid month.
workspaces[].changesAtstring (date-time) | nullno
workspaces[].endsAtstring (date-time) | nullnoSet when you stopped paying for it.
workspaces[].archivedbooleanno
workspaces[].primarybooleannoThe customer's first workspace.
workspaces[].joinsAtstring (date-time) | nullnoFor a customer that joined you from its own billing: when this workspace moves to your bill, at the end of the month its Owner already paid for. Until then it is not on your bill and its plan cannot be changed (partner.workspace_joining).
workspaces[].addOnsarray of stringno
workspaces[].usageobject | nullnoThis billing month's counts. Never content.
workspaces[].usage.cycleStartstring (date-time) | nullno
workspaces[].usage.predictionsintegerno
workspaces[].usage.predictionsLimitinteger | nullno
workspaces[].usage.picturesintegerno
workspaces[].usage.picturesLimitinteger | nullno
ownerSignedInAtstring (date-time) | nullnoA resold customer only. When its Owner last signed in; null until they have.
Errors: 404, 409, 401, 403, 429

404 — No such customer, or no such workspace of it.

{
  "error": "No such workspace for this customer.",
  "code": "partner.workspace_not_found"
}

409 — It is the first workspace, or it is already ending.

{
  "error": "That is the customer’s first workspace. To stop paying for it, cancel the customer.",
  "code": "partner.workspace_primary"
}

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

POST /api/v1/customers/{customerId}/hand-over

Hand the customer over (it goes direct)

Your key stops reaching the account at once and the customer leaves your list. The plan runs to the end of the month you paid for; then the customer pays StayCharted itself. An access: api customer's contact email (the one on file) gets a link to set up a login. A resold customer keeps its login and is told billing is now theirs.

If the contact's address belongs to a StayCharted login that never confirmed it, nothing changes (409 partner.contact_unconfirmed): the address is asked to confirm first, then you call this again.

Path parameters

NameTypeRequiredDescription
customerIdstring (uuid)yesThe customer's id, from GET /api/v1/customers.

Responses

200 — Handed over.

FieldTypeRequiredNotes
idstring (uuid)yes
statusstringyes(one of handed_over)
contactEmailstringno
paidUntilstring (date-time) | nullno
Errors: 409, 401, 403, 404, 429

409 — The contact has not confirmed the address, or another change to your bill is going through.

{
  "error": "The contact’s address belongs to a StayCharted login that has not confirmed it.",
  "code": "partner.contact_unconfirmed"
}

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

404 — No customer of yours with that id.

{
  "error": "No such customer of this partner.",
  "code": "partner.customer_not_found"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

POST /api/v1/customers/{customerId}/cancel

Cancel the customer

Every workspace runs to the end of the paid month, and your bill drops from the next invoice. For 30 days after that your key can still download the customer's export, and nothing else; then the account is closed and its data deleted.

Path parameters

NameTypeRequiredDescription
customerIdstring (uuid)yesThe customer's id, from GET /api/v1/customers.

Responses

200 — The customer, now cancelling, with endsAt.

FieldTypeRequiredNotes
idstring (uuid)yes
namestringyes
contactEmailstringno
accessstringyes(one of api, login)
joinedFromDirectbooleannoThe customer already paid StayCharted itself and asked to be billed through you. Its account was its own before you, so cancelling it moves it back to direct billing rather than closing it.
statusstringyes(one of active, cancelling, cancelled, handed_over)
planstring | nullnoThe first workspace's plan.
pendingPlanstring | nullno
addOnsarray of stringno
endsAtstring (date-time) | nullno
createdAtstring (date-time)no
usageobject | nullnoThis billing month's counts. Never content.
usage.cycleStartstring (date-time) | nullno
usage.predictionsintegerno
usage.predictionsLimitinteger | nullno
usage.picturesintegerno
usage.picturesLimitinteger | nullno
workspacesarray of objectno
workspaces[].idstring (uuid)no
workspaces[].namestringno
workspaces[].planstringno
workspaces[].pendingPlanstring | nullnoA downgrade waiting for the end of the paid month.
workspaces[].changesAtstring (date-time) | nullno
workspaces[].endsAtstring (date-time) | nullnoSet when you stopped paying for it.
workspaces[].archivedbooleanno
workspaces[].primarybooleannoThe customer's first workspace.
workspaces[].joinsAtstring (date-time) | nullnoFor a customer that joined you from its own billing: when this workspace moves to your bill, at the end of the month its Owner already paid for. Until then it is not on your bill and its plan cannot be changed (partner.workspace_joining).
workspaces[].addOnsarray of stringno
workspaces[].usageobject | nullnoThis billing month's counts. Never content.
workspaces[].usage.cycleStartstring (date-time) | nullno
workspaces[].usage.predictionsintegerno
workspaces[].usage.predictionsLimitinteger | nullno
workspaces[].usage.picturesintegerno
workspaces[].usage.picturesLimitinteger | nullno
ownerSignedInAtstring (date-time) | nullnoA resold customer only. When its Owner last signed in; null until they have.
Errors: 409, 401, 403, 404, 429

409 — Already being cancelled, or another change to your bill is going through.

{
  "error": "That customer is already being cancelled.",
  "code": "partner.already_cancelling"
}

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

404 — No customer of yours with that id.

{
  "error": "No such customer of this partner.",
  "code": "partner.customer_not_found"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

POST /api/v1/customers/{customerId}/resend-link

Send a resold customer's set-up link again

Only while the customer is active and nobody has signed in as its Owner yet.

Path parameters

NameTypeRequiredDescription
customerIdstring (uuid)yesThe customer's id, from GET /api/v1/customers.

Responses

200 — Sent to the contact email on file.

FieldTypeRequiredNotes
sentbooleanno
tostringno
kindstringnoWhich email was sent.
Errors: 409, 401, 403, 404, 429

409 — Not a resold customer that is still active, or its Owner has signed in already.

Example — no_link

{
  "error": "Only a resold customer that is still active has a set-up link.",
  "code": "partner.no_setup_link"
}

Example — signed_in

{
  "error": "Someone has already signed in as this customer’s Owner, so there is no set-up link to send.",
  "code": "partner.owner_signed_in"
}

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

404 — No customer of yours with that id.

{
  "error": "No such customer of this partner.",
  "code": "partner.customer_not_found"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

GET /api/v1/customers/{customerId}/export

Download the customer's data

The same export an account Owner downloads in the app: models, training data, filled files and Activity, as a ZIP. Available while you manage the customer and for 30 days after a cancellation ends. Not for a resold customer, whose Owner exports it.

Path parameters

NameTypeRequiredDescription
customerIdstring (uuid)yesThe customer's id, from GET /api/v1/customers.

Responses

200 — The export.

A file: application/zip.

Errors: 401, 403, 404, 429

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

404 — No customer of yours with that id.

{
  "error": "No such customer of this partner.",
  "code": "partner.customer_not_found"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

Models

Build, train, test and publish a customer's models.

GET /api/v1/customers/{customerId}/models

The customer's models

Every model, published or not.

Path parameters

NameTypeRequiredDescription
customerIdstring (uuid)yesThe customer's id, from GET /api/v1/customers.

Responses

200 — The models.

FieldTypeRequiredNotes
modelsarray of objectyes
models[].idstring (uuid)no
models[].namestringno
models[].descriptionstringno
models[].modalitystringno(one of text, image)
models[].statusstringnoWhere the model is, as the app's Models screen says it.
models[].publishedbooleanno
models[].liveVersionstring (uuid) | nullno
models[].latestVersionstring (uuid) | nullno
models[].accuracynumber | nullnoThe latest version's accuracy on held-out rows, 0 to 1.
Errors: 401, 403, 404, 429

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

404 — No customer of yours with that id.

{
  "error": "No such customer of this partner.",
  "code": "partner.customer_not_found"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

POST /api/v1/customers/{customerId}/models

Create a model

Then send its training data (/data), check it (/quality), train (/train), read the report (/jobs/{jobId}) and publish (/publish).

Path parameters

NameTypeRequiredDescription
customerIdstring (uuid)yesThe customer's id, from GET /api/v1/customers.

Request body (application/json)

FieldTypeRequiredNotes
namestringyes
descriptionstringno
modalitystringnoimage for a model that reads pictures (the customer's plan needs Vision). (one of text, image)
{
  "name": "Ticket routing",
  "description": "Which team a new ticket goes to.",
  "modality": "text"
}

Responses

201 — Created, with no data yet.

FieldTypeRequiredNotes
idstring (uuid)no
namestringno
descriptionstringno
modalitystringno(one of text, image)
statusstringnoWhere the model is, as the app's Models screen says it.
publishedbooleanno
liveVersionstring (uuid) | nullno
latestVersionstring (uuid) | nullno
accuracynumber | nullnoThe latest version's accuracy on held-out rows, 0 to 1.
Errors: 400, 402, 401, 403, 404, 429

400 — No name.

{
  "error": "Give the model a name.",
  "code": "partner.model_name"
}

402 — The customer's plan does not allow this, or a monthly allowance is spent (resetsAt says when it refills).

{
  "error": "This needs 40 predictions and 12 remain this billing period.",
  "code": "plan.prediction_limit",
  "entitlement": "predictionsPerMonth",
  "limit": 10000,
  "used": 9988,
  "resetsAt": "2026-11-26T00:00:00.000Z"
}

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

404 — No customer of yours with that id.

{
  "error": "No such customer of this partner.",
  "code": "partner.customer_not_found"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

GET /api/v1/customers/{customerId}/models/{modelId}

One model and its versions

Path parameters

NameTypeRequiredDescription
customerIdstring (uuid)yesThe customer's id, from GET /api/v1/customers.
modelIdstring (uuid)yes

Responses

200 — The model, with every training run.

A Model, with every training run.

FieldTypeRequiredNotes
idstring (uuid)no
namestringno
descriptionstringno
modalitystringno(one of text, image)
statusstringno
publishedbooleanno
liveVersionstring (uuid) | nullno
latestVersionstring (uuid) | nullno
accuracynumber | nullno
versionsarray of objectno
versions[].idstring (uuid)no
versions[].statusstringno
versions[].enginestring | nullno
versions[].accuracynumber | nullno
versions[].startedAtstring (date-time) | nullno
versions[].livebooleanno
Errors: 401, 403, 404, 429

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

404 — No such customer of yours, or no such model of that customer.

{
  "error": "No such model for this customer.",
  "code": "partner.model_not_found"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

PATCH /api/v1/customers/{customerId}/models/{modelId}

Rename or describe a model

Path parameters

NameTypeRequiredDescription
customerIdstring (uuid)yesThe customer's id, from GET /api/v1/customers.
modelIdstring (uuid)yes

Request body (application/json)

FieldTypeRequiredNotes
namestringno
descriptionstringno

Responses

200 — The model, changed.

FieldTypeRequiredNotes
idstring (uuid)no
namestringno
descriptionstringno
modalitystringno(one of text, image)
statusstringnoWhere the model is, as the app's Models screen says it.
publishedbooleanno
liveVersionstring (uuid) | nullno
latestVersionstring (uuid) | nullno
accuracynumber | nullnoThe latest version's accuracy on held-out rows, 0 to 1.
Errors: 401, 403, 404, 429

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

404 — No such customer of yours, or no such model of that customer.

{
  "error": "No such model for this customer.",
  "code": "partner.model_not_found"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

DELETE /api/v1/customers/{customerId}/models/{modelId}

Delete a model

The model, its versions and its training data. It cannot be undone.

Path parameters

NameTypeRequiredDescription
customerIdstring (uuid)yesThe customer's id, from GET /api/v1/customers.
modelIdstring (uuid)yes

Responses

200 — Deleted.

FieldTypeRequiredNotes
deletedbooleanno
idstring (uuid)no
Errors: 401, 403, 404, 429

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

404 — No such customer of yours, or no such model of that customer.

{
  "error": "No such model for this customer.",
  "code": "partner.model_not_found"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

POST /api/v1/customers/{customerId}/models/{modelId}/columns

Which columns say something about the answer

Optional, before sending data. Measures each column against the answers on up to 2,000 rows that have one, and recommends which ones the model should read. Nothing is stored.

Path parameters

NameTypeRequiredDescription
customerIdstring (uuid)yesThe customer's id, from GET /api/v1/customers.
modelIdstring (uuid)yes

Request body (application/json)

FieldTypeRequiredNotes
columnsarray of stringyesThe column names, unique, up to 200.
rowsarray of array of stringyesRows of values, each with one value per column, in the order of columns. Values are read as text.
labelColumnstringyesThe column that holds the answer.

Responses

FieldTypeRequiredNotes
labelColumnstringno
rowsMeasuredintegerno
recommendedarray of stringno
columnsarray of objectno
columns[].columnstringno
columns[].verdictstringno
columns[].reasonstringno
Errors: 400, 401, 403, 404, 429

400 — The rows or columns are not usable.

Example — rows

{
  "error": "There are no rows to send.",
  "code": "partner.rows"
}

Example — label

{
  "error": "Choose the column that holds the answers.",
  "code": "partner.label_column"
}

Example — inputs

{
  "error": "Choose at least one column for the model to read.",
  "code": "partner.input_columns"
}

Example — answers

{
  "error": "No row has an answer in that column yet.",
  "code": "partner.no_answers"
}

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

404 — No such customer of yours, or no such model of that customer.

{
  "error": "No such model for this customer.",
  "code": "partner.model_not_found"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

POST /api/v1/customers/{customerId}/models/{modelId}/data

Send the training data

Two ways:

Sending data again replaces the model's training data.

Path parameters

NameTypeRequiredDescription
customerIdstring (uuid)yesThe customer's id, from GET /api/v1/customers.
modelIdstring (uuid)yes

Request body (application/json)

FieldTypeRequiredNotes
columnsarray of stringyesThe column names, unique, up to 200.
rowsarray of array of stringyesRows of values, each with one value per column, in the order of columns. Values are read as text.
inputColumnsarray of stringyesThe columns the model reads.
labelColumnstringyesThe column that holds the answer.
{
  "columns": [
    "Subject",
    "Body",
    "Team"
  ],
  "rows": [
    [
      "Charged twice",
      "My card shows two payments for one order.",
      "Billing"
    ],
    [
      "Cannot sign in",
      "The reset link says it has expired.",
      "Account access"
    ]
  ],
  "inputColumns": [
    "Subject",
    "Body"
  ],
  "labelColumn": "Team"
}

Or a file, as multipart/form-data with the field file.

Responses

200 — For JSON rows, the data-quality summary. For a file, its columns, ready for /mapping.

JSON rows: rowsSent plus the Quality fields. A file: uploaded, columns, rows, and imageColumns for a picture file.

FieldTypeRequiredNotes
rowsSentintegerno
uploadedbooleanno
columnsarray of stringno
rowsinteger | nullno
imageColumnsarray of stringno
usableRowsinteger | nullno
categoryCountinteger | nullno
distributionarray of objectno
duplicateCountintegerno
rareCategoryCountintegerno
verdictstring | nullno
reasonsarray of objectno
Errors: 400, 402, 401, 403, 404, 429

400 — The rows or columns are not usable.

Example — rows

{
  "error": "There are no rows to send.",
  "code": "partner.rows"
}

Example — label

{
  "error": "Choose the column that holds the answers.",
  "code": "partner.label_column"
}

Example — inputs

{
  "error": "Choose at least one column for the model to read.",
  "code": "partner.input_columns"
}

Example — answers

{
  "error": "No row has an answer in that column yet.",
  "code": "partner.no_answers"
}

402 — The customer's plan does not allow this, or a monthly allowance is spent (resetsAt says when it refills).

{
  "error": "This needs 40 predictions and 12 remain this billing period.",
  "code": "plan.prediction_limit",
  "entitlement": "predictionsPerMonth",
  "limit": 10000,
  "used": 9988,
  "resetsAt": "2026-11-26T00:00:00.000Z"
}

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

404 — No such customer of yours, or no such model of that customer.

{
  "error": "No such model for this customer.",
  "code": "partner.model_not_found"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

POST /api/v1/customers/{customerId}/models/{modelId}/mapping

Choose the columns, after sending a file

A text model: inputColumns and labelColumn. A picture model: imageColumn, and where the category comes from (labelColumn, or a labelRule such as the folder name), plus any textColumns it should read with each picture. Picture data is then imported in the background (importing: true); read /quality when it is done.

Path parameters

NameTypeRequiredDescription
customerIdstring (uuid)yesThe customer's id, from GET /api/v1/customers.
modelIdstring (uuid)yes

Request body (application/json)

FieldTypeRequiredNotes
inputColumnsarray of stringno
labelColumnstringno
imageColumnstringno
labelRuleobjectno
textColumnsarray of stringno

Responses

200 — A text model's quality summary, or a picture model's import status.

FieldTypeRequiredNotes
acceptedbooleanno
statusstring | nullno
importingbooleanno
usableRowsinteger | nullno
categoryCountinteger | nullno
verdictstring | nullno
Errors: 401, 403, 404, 429

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

404 — No such customer of yours, or no such model of that customer.

{
  "error": "No such model for this customer.",
  "code": "partner.model_not_found"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

GET /api/v1/customers/{customerId}/models/{modelId}/quality

The data-quality findings

The same findings as the app's Check data step. Pass version back with a fix, so a fix is never applied to data that has changed since.

Path parameters

NameTypeRequiredDescription
customerIdstring (uuid)yesThe customer's id, from GET /api/v1/customers.
modelIdstring (uuid)yes

Responses

200 — The findings.

FieldTypeRequiredNotes
versionstring | nullnoPass back with a fix.
usableRowsinteger | nullno
categoryCountinteger | nullno
distributionarray of objectnoRows per category.
duplicateCountintegerno
rareCategoryCountintegerno
verdictstring | nullnoWhether the data is ready to train, as the app's Check data step says.
reasonsarray of objectno
duplicatesarray of objectno
Errors: 401, 403, 404, 429

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

404 — No such customer of yours, or no such model of that customer.

{
  "error": "No such model for this customer.",
  "code": "partner.model_not_found"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

POST /api/v1/customers/{customerId}/models/{modelId}/quality

Merge categories, leave some out, or remove duplicates

Send exactly one of mergeLabels, excludeCategories or removeDuplicates, with the version from GET.

Path parameters

NameTypeRequiredDescription
customerIdstring (uuid)yesThe customer's id, from GET /api/v1/customers.
modelIdstring (uuid)yes

Request body (application/json)

FieldTypeRequiredNotes
versionstringno
mergeLabelsarray of objectnoCategories that are the same thing, folded into one.
mergeLabels[].canonicalstringyes
mergeLabels[].variantsarray of stringyes
excludeCategoriesarray of stringnoCategories to leave out of training, every row of them.
removeDuplicatesarray of integernoIndexes of duplicate rows to remove, as GET lists them.
{
  "version": "2026-10-26T09:07:12.418Z",
  "mergeLabels": [
    {
      "canonical": "Billing",
      "variants": [
        "billing",
        "Invoices"
      ]
    }
  ]
}

Responses

200 — The quality summary after the change.

FieldTypeRequiredNotes
versionstring | nullnoPass back with a fix.
usableRowsinteger | nullno
categoryCountinteger | nullno
distributionarray of objectnoRows per category.
duplicateCountintegerno
rareCategoryCountintegerno
verdictstring | nullnoWhether the data is ready to train, as the app's Check data step says.
reasonsarray of objectno
duplicatesarray of objectno
Errors: 400, 401, 403, 404, 409, 429

400 — None of the three was sent.

{
  "error": "Send mergeLabels, excludeCategories or removeDuplicates.",
  "code": "partner.quality_action"
}

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

404 — No such customer of yours, or no such model of that customer.

{
  "error": "No such model for this customer.",
  "code": "partner.model_not_found"
}

409 — The data changed since version; read it again.

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

POST /api/v1/customers/{customerId}/models/{modelId}/train

Start training

202 with a job id; training runs on a queue. Poll /jobs/{jobId} every 15 to 60 seconds.

Engines, by what the customer's plan includes:

Path parameters

NameTypeRequiredDescription
customerIdstring (uuid)yesThe customer's id, from GET /api/v1/customers.
modelIdstring (uuid)yes

Request body (application/json)

FieldTypeRequiredNotes
enginestringyes(one of classifier, ai_classifier, ai_image_classifier, dedicated)
{
  "engine": "ai_classifier"
}

Responses

202 — Queued.

FieldTypeRequiredNotes
jobIdstring (uuid) | nullno
statusstringno
enginestringno
Errors: 400, 402, 401, 403, 404, 429

400 — An engine this model cannot use, or no Dedicated training for this customer.

Example — engine

{
  "error": "Choose the engine: classifier, ai_classifier, dedicated.",
  "code": "partner.engine"
}

Example — dedicated

{
  "error": "Dedicated AI training is not available for this customer.",
  "code": "partner.dedicated_unavailable"
}

402 — The customer's plan does not allow this, or a monthly allowance is spent (resetsAt says when it refills).

{
  "error": "This needs 40 predictions and 12 remain this billing period.",
  "code": "plan.prediction_limit",
  "entitlement": "predictionsPerMonth",
  "limit": 10000,
  "used": 9988,
  "resetsAt": "2026-11-26T00:00:00.000Z"
}

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

404 — No such customer of yours, or no such model of that customer.

{
  "error": "No such model for this customer.",
  "code": "partner.model_not_found"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

GET /api/v1/customers/{customerId}/models/{modelId}/jobs/{jobId}

Training progress, then the report

When status is completed, report holds the test on rows the model never trained on.

Path parameters

NameTypeRequiredDescription
customerIdstring (uuid)yesThe customer's id, from GET /api/v1/customers.
modelIdstring (uuid)yes
jobIdstring (uuid)yes

Responses

200 — The run.

FieldTypeRequiredNotes
jobIdstring (uuid)no
statusstringnoqueued, running, completed, failed or cancelled.
stagestring | nullnoWhat it is doing now, in words.
failurestring | nullno
reportobject | nullnoThe test on held-out rows, once completed.
report.versionIdstring (uuid)no
report.isLivebooleanno
report.accuracynumber | nullno
report.baselinenumber | nullnoAlways guessing the commonest category.
report.testedOninteger | nullno
report.trainedOninteger | nullno
report.perCategoryarray of objectno
report.perCategory[].categorystringno
report.perCategory[].accuracynumberno
report.perCategory[].correctintegerno
report.perCategory[].totalintegerno
Errors: 404, 401, 403, 429

404 — No such run for this model.

{
  "error": "No such training run.",
  "code": "partner.job_not_found"
}

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

POST /api/v1/customers/{customerId}/models/{modelId}/publish

Make a trained version live

Without jobId, the latest completed version. The previous live version stops answering.

Path parameters

NameTypeRequiredDescription
customerIdstring (uuid)yesThe customer's id, from GET /api/v1/customers.
modelIdstring (uuid)yes

Request body (application/json)

FieldTypeRequiredNotes
jobIdstring (uuid)no

Responses

200 — Live.

FieldTypeRequiredNotes
publishedbooleanno
jobIdstring (uuid) | nullno
Errors: 402, 401, 403, 404, 429

402 — The customer's plan does not allow this, or a monthly allowance is spent (resetsAt says when it refills).

{
  "error": "This needs 40 predictions and 12 remain this billing period.",
  "code": "plan.prediction_limit",
  "entitlement": "predictionsPerMonth",
  "limit": 10000,
  "used": 9988,
  "resetsAt": "2026-11-26T00:00:00.000Z"
}

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

404 — No such customer of yours, or no such model of that customer.

{
  "error": "No such model for this customer.",
  "code": "partner.model_not_found"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

Predictions

Ask a customer's published model, one record or a batch.

POST /api/v1/customers/{customerId}/models/{modelId}/predict

Ask the published model, one record or many

As the Prediction API's predict (openapi.yaml), under the customer's path. input for one record, inputs for up to 100. Each record is a string, an object of the fields the model reads, or for a picture model a link or {"imageBase64": "..."}. Counted against the customer's monthly predictions; the whole request is checked first, so it is refused whole rather than half served.

Path parameters

NameTypeRequiredDescription
customerIdstring (uuid)yesThe customer's id, from GET /api/v1/customers.
modelIdstring (uuid)yes

Request body (application/json)

Exactly one of input or inputs; the server enforces it.

FieldTypeRequiredNotes
inputanynoOne record. A string, an object of the fields the model reads, or for a picture model a link or {"imageBase64":"..."}.
inputsarray of anynoUp to 100 records, each as input takes one (up to 20 pictures). (minItems: 1, maxItems: 100)
{
  "inputs": [
    "My card was charged twice for the same order",
    "The parcel arrived damaged"
  ]
}

Responses

200 — One prediction per record, in the order sent.

FieldTypeRequiredNotes
modelobjectno
model.idstring (uuid)no
model.namestringno
predictionsarray of objectno
predictions[].inputanynoThe record, echoed back.
predictions[].labelstring | nullno
predictions[].confidencenumber | nullno0 to 1.
predictions[].needsReviewbooleannoBelow the model's review cutoff; the answer went to the Review Queue.
predictions[].labelsarray of stringnoFor a model whose answer is a list.
predictions[].fieldsobjectnoFor a model whose answer has several columns.
predictions[].errorstringnoA picture that could not be used, as a code.
predictions[].messagestringno
usageobjectno
usage.predictionsintegernoRecords counted by this request.
Errors: 400, 402, 409, 401, 403, 404, 429, 503

400 — The records are missing or not the shape the model reads.

{
  "error": "Send \"input\" with one string, or \"inputs\" with an array of them.",
  "code": "api.input_required"
}

402 — The customer's plan does not allow this, or a monthly allowance is spent (resetsAt says when it refills).

{
  "error": "This needs 40 predictions and 12 remain this billing period.",
  "code": "plan.prediction_limit",
  "entitlement": "predictionsPerMonth",
  "limit": 10000,
  "used": 9988,
  "resetsAt": "2026-11-26T00:00:00.000Z"
}

409 — The model is not published, or needs retraining.

{
  "error": "That model is not deployed yet.",
  "code": "api.model_not_deployed"
}

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

404 — No such customer of yours, or no such model of that customer.

{
  "error": "No such model for this customer.",
  "code": "partner.model_not_found"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

503 — A service the model depends on is restarting. Nothing was counted; retry after a short wait.

{
  "error": "This model is temporarily unable to answer. Try again in a few minutes.",
  "code": "api.model_unavailable"
}

POST /api/v1/customers/{customerId}/models/{modelId}/classify

Ask the published model one record, flat answer

One record, and every value of the answer a top-level key, for tools that bind fields from a list.

Path parameters

NameTypeRequiredDescription
customerIdstring (uuid)yesThe customer's id, from GET /api/v1/customers.
modelIdstring (uuid)yes

Request body (application/json)

FieldTypeRequiredNotes
inputanyyesA string, an object of the fields the model reads, or for a picture model a link or {"imageBase64":"..."} (with "text" for a model that also reads text).
{
  "input": "My card was charged twice for the same order"
}

Responses

200 — The answer.

FieldTypeRequiredNotes
labelstring | nullno
confidencenumber | nullno
needsReviewbooleanno
labelsarray of stringno
fieldsobjectno
modelIdstring (uuid)no
modelNamestringno
errorstringno
messagestringno
{
  "label": "Billing",
  "confidence": 0.94,
  "needsReview": false,
  "modelId": "8f14e45f-ceea-467a-9b2c-1d5c2b0e7a11",
  "modelName": "Ticket routing"
}
Errors: 400, 402, 409, 401, 403, 404, 429, 503

400 — No input.

{
  "error": "Send \"input\" with the text to classify.",
  "code": "api.input_missing"
}

402 — The customer's plan does not allow this, or a monthly allowance is spent (resetsAt says when it refills).

{
  "error": "This needs 40 predictions and 12 remain this billing period.",
  "code": "plan.prediction_limit",
  "entitlement": "predictionsPerMonth",
  "limit": 10000,
  "used": 9988,
  "resetsAt": "2026-11-26T00:00:00.000Z"
}

409 — The model is not published, or needs retraining.

{
  "error": "That model is not deployed yet.",
  "code": "api.model_not_deployed"
}

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

404 — No such customer of yours, or no such model of that customer.

{
  "error": "No such model for this customer.",
  "code": "partner.model_not_found"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

503 — A service the model depends on is restarting. Nothing was counted; retry after a short wait.

{
  "error": "This model is temporarily unable to answer. Try again in a few minutes.",
  "code": "api.model_unavailable"
}

POST /api/v1/customers/{customerId}/models/{modelId}/fills

Start a batch fill

For many records at once: 202 with a fill id, then poll /fills/{fillId}. Send JSON rows (each an object of the columns the model reads), or a file (multipart/form-data, field file): a CSV, a spreadsheet, or for a picture model a ZIP of pictures. A file can say which column to write the answer into with ?targetColumn=; the default is "StayCharted category". Counted against the customer's monthly predictions, like a fill in the app.

Path parameters

NameTypeRequiredDescription
customerIdstring (uuid)yesThe customer's id, from GET /api/v1/customers.
modelIdstring (uuid)yes

Query parameters

NameTypeRequiredDescription
targetColumnstringnoFor a file, the name of the answer column. JSON rows always use "StayCharted category".

Request body (application/json)

FieldTypeRequiredNotes
rowsarray of objectyes
{
  "rows": [
    {
      "Subject": "Charged twice",
      "Body": "My card shows two payments for one order."
    }
  ]
}

Or a file, as multipart/form-data with the field file.

Responses

202 — Queued.

FieldTypeRequiredNotes
fillIdstring (uuid) | nullno
statusstringno
rowsintegerno
Errors: 400, 402, 401, 403, 404, 429

400 — No rows, or rows that are not records.

{
  "error": "Send rows: a list of records, each one the columns the model reads.",
  "code": "partner.fill_rows"
}

402 — The customer's plan does not allow this, or a monthly allowance is spent (resetsAt says when it refills).

{
  "error": "This needs 40 predictions and 12 remain this billing period.",
  "code": "plan.prediction_limit",
  "entitlement": "predictionsPerMonth",
  "limit": 10000,
  "used": 9988,
  "resetsAt": "2026-11-26T00:00:00.000Z"
}

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

404 — No such customer of yours, or no such model of that customer.

{
  "error": "No such model for this customer.",
  "code": "partner.model_not_found"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

GET /api/v1/customers/{customerId}/models/{modelId}/fills/{fillId}

Fill progress, then the answers

When status is completed, answers holds one per row in the order sent, and file the path of the filled file.

Path parameters

NameTypeRequiredDescription
customerIdstring (uuid)yesThe customer's id, from GET /api/v1/customers.
modelIdstring (uuid)yes
fillIdstring (uuid)yes

Responses

200 — The fill.

FieldTypeRequiredNotes
fillIdstring (uuid)no
statusstringnoqueued, running, completed, failed or cancelled.
filledinteger | nullno
totalinteger | nullno
failurestring | nullno
answersarray of objectnoOnce completed, one per row, in the order sent.
answers[].categorystring | nullno
answers[].confidencenumber | nullno
filestringnoOnce completed, the path of the filled file.
Errors: 404, 401, 403, 429

404 — No such fill for this model.

{
  "error": "No such fill.",
  "code": "partner.fill_not_found"
}

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

GET /api/v1/customers/{customerId}/models/{modelId}/fills/{fillId}/file

Download the filled file

The rows as sent, with the answer, its confidence and whether it went for review in new columns, in the format sent.

Path parameters

NameTypeRequiredDescription
customerIdstring (uuid)yesThe customer's id, from GET /api/v1/customers.
modelIdstring (uuid)yes
fillIdstring (uuid)yes

Responses

200 — The filled file.

A file: application/octet-stream.

Errors: 404, 401, 403, 429

404 — No such fill, or it is not finished.

{
  "error": "No such fill.",
  "code": "partner.fill_not_found"
}

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

Review Queue

Answers the model was unsure of, for a person to confirm.

GET /api/v1/customers/{customerId}/models/{modelId}/review

Answers waiting for a person

Answers below the model's review cutoff, from every channel (API, fills), with the same question grouped once. Least confident first.

Path parameters

NameTypeRequiredDescription
customerIdstring (uuid)yesThe customer's id, from GET /api/v1/customers.
modelIdstring (uuid)yes

Query parameters

NameTypeRequiredDescription
statestringno
offsetintegerno
limitintegerno

Responses

200 — A page of the queue.

FieldTypeRequiredNotes
openboolean | nullnoWhether the customer's plan has the Review Queue.
cutoffnumber | nullnoAnswers below this confidence come here.
countsobjectno
totalintegerno
categoriesarray of stringno
itemsarray of objectno
items[].groupKeystringnoPass back to answer it.
items[].itemIdstringno
items[].sizeintegernoHow many times this same question was asked.
items[].confidencenumber | nullno
items[].predictedLabelstring | nullno
items[].reviewedLabelstring | nullno
items[].sourcesarray of stringnoThe channels it came from.
items[].textstring | nullno
items[].inputTextstring | nullno
items[].imageHashstring | nullno
items[].firstAtstring (date-time)no
items[].alternativesarray of object | nullnoThe model's next candidates.
Errors: 402, 401, 403, 404, 429

402 — The customer's plan does not allow this, or a monthly allowance is spent (resetsAt says when it refills).

{
  "error": "This needs 40 predictions and 12 remain this billing period.",
  "code": "plan.prediction_limit",
  "entitlement": "predictionsPerMonth",
  "limit": 10000,
  "used": 9988,
  "resetsAt": "2026-11-26T00:00:00.000Z"
}

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

404 — No such customer of yours, or no such model of that customer.

{
  "error": "No such model for this customer.",
  "code": "partner.model_not_found"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

POST /api/v1/customers/{customerId}/models/{modelId}/review/answer

Answer one

The right category in label (pass the model's predictedLabel too, so a correction is recorded as one), or action: skip or action: unsure. A confirmed answer is used the next time the model is retrained.

Path parameters

NameTypeRequiredDescription
customerIdstring (uuid)yesThe customer's id, from GET /api/v1/customers.
modelIdstring (uuid)yes

Request body (application/json)

FieldTypeRequiredNotes
groupKeystringyesFrom the queue's items.
labelstringno
predictedLabelstringno
actionstringno(one of skip, unsure)
{
  "groupKey": "7d1c0a2e9b",
  "label": "Billing",
  "predictedLabel": "Account access"
}

Responses

200 — How many rows the answer covered, and the queue's counts.

FieldTypeRequiredNotes
changedintegerno
countsobjectno
Errors: 400, 409, 401, 403, 404, 429

400 — No group, or no category.

{
  "error": "Choose the right category, or skip it.",
  "code": "review.label_required"
}

409 — Someone answered it already.

{
  "error": "That answer was already given, or is already in a training run. Reload to see where it is.",
  "code": "review.already_answered"
}

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

404 — No such customer of yours, or no such model of that customer.

{
  "error": "No such model for this customer.",
  "code": "partner.model_not_found"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

POST /api/v1/customers/{customerId}/models/{modelId}/review/retrain

Retrain with the confirmed answers

Adds the confirmed answers to the training data and starts a new version (202), on the same engine. Publishing it is a separate call, after you have read its report.

Path parameters

NameTypeRequiredDescription
customerIdstring (uuid)yesThe customer's id, from GET /api/v1/customers.
modelIdstring (uuid)yes

Responses

200 — Nothing to train on after all; the answers were settled.

202 — Training started.

FieldTypeRequiredNotes
jobIdstring (uuid)no
statusstringno
Errors: 400, 402, 401, 403, 404, 429

400 — No confirmed answers yet, or the model was never trained.

{
  "error": "No reviewed answers are waiting to be trained on.",
  "code": "review.nothing_ready"
}

402 — The customer's plan does not allow this, or a monthly allowance is spent (resetsAt says when it refills).

{
  "error": "This needs 40 predictions and 12 remain this billing period.",
  "code": "plan.prediction_limit",
  "entitlement": "predictionsPerMonth",
  "limit": 10000,
  "used": 9988,
  "resetsAt": "2026-11-26T00:00:00.000Z"
}

401 — No partner key, or not a valid one. One answer for every kind of bad key; an address that keeps sending bad keys is told to wait (429 partner.too_many_refusals).

Example — missing

{
  "error": "Send your partner key as: Authorization: Bearer amt_partner_….",
  "code": "partner.key_missing"
}

Example — invalid

{
  "error": "That partner key is not valid.",
  "code": "partner.key_invalid"
}

403 — The partner is suspended, the call came from an address the key does not allow, or the customer is resold and signs in itself.

Example — suspended

{
  "error": "This partner’s access is suspended. Contact partners@staycharted.com.",
  "code": "partner.suspended"
}

Example — address

{
  "error": "This key is not allowed from this address.",
  "code": "partner.address_not_allowed"
}

Example — signs_in

{
  "error": "This customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.",
  "code": "partner.customer_signs_in"
}

404 — No such customer of yours, or no such model of that customer.

{
  "error": "No such model for this customer.",
  "code": "partner.model_not_found"
}

429 — Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterSeconds to wait.

Example — key

{
  "error": "A partner key may make 600 calls a minute. Slow down and retry.",
  "code": "partner.rate_limited",
  "retryAfter": 12
}

Example — refusals

{
  "error": "Too many requests with a missing or invalid partner key from this address. Wait a minute.",
  "code": "partner.too_many_refusals",
  "retryAfter": 60
}

Error codes

Every error is JSON with a stable code. Branch on the code; the error sentence is for people and may be reworded.

HTTPCodeWhat it means
400api.input_missingSend "input" with the text to classify.
400api.input_requiredSend "input" with one string, or "inputs" with an array of them.
400partner.addonThe faces add-on cannot be added: there is no such add-on.
400partner.contact_emailGive the customer contact’s email address.
400partner.customer_nameGive the customer a name.
400partner.dedicated_unavailableDedicated AI training is not available for this customer.
400partner.engineChoose the engine: classifier, ai_classifier, dedicated.
400partner.fill_rowsSend rows: a list of records, each one the columns the model reads.
400partner.input_columnsChoose at least one column for the model to read.
400partner.label_columnChoose the column that holds the answers.
400partner.model_nameGive the model a name.
400partner.no_answersNo row has an answer in that column yet.
400partner.planChoose a plan for the customer: "essentials" or "business".
400partner.plan_no_apiThat plan does not include the API, so a partner's product cannot use it.
400partner.quality_actionSend mergeLabels, excludeCategories or removeDuplicates.
400partner.rowsThere are no rows to send.
400partner.workspace_nameGive the workspace a name.
400review.label_requiredChoose the right category, or skip it.
400review.nothing_readyNo reviewed answers are waiting to be trained on.
401partner.key_invalidThat partner key is not valid.
401partner.key_missingSend your partner key as: Authorization: Bearer amt_partner_….
402partner.card_requiredThere is no card on your partner account yet. Add one on your Partner page in StayCharted, then try again.
402partner.payment_declinedYour card was not charged, so nothing was created or changed. Update the card on your Partner page in StayCharted, then try again.
402plan.prediction_limitThis needs 40 predictions and 12 remain this billing period.
403partner.address_not_allowedThis key is not allowed from this address.
403partner.customer_signs_inThis customer’s own people sign in and use its models, so a partner key reaches its plan and workspaces only.
403partner.suspendedThis partner’s access is suspended. Contact partners@staycharted.com.
404partner.customer_not_foundNo such customer of this partner.
404partner.fill_not_foundNo such fill.
404partner.job_not_foundNo such training run.
404partner.model_not_foundNo such model for this customer.
404partner.workspace_not_foundNo such workspace for this customer.
409api.model_not_deployedThat model is not deployed yet.
409partner.already_cancellingThat customer is already being cancelled.
409partner.busyAnother change to this partner’s bill is still going through. Try again in a minute.
409partner.contact_unconfirmedThe contact’s address belongs to a StayCharted login that has not confirmed it.
409partner.customer_endingThat customer is being cancelled, so its plan cannot change.
409partner.customer_names_itselfThis customer signs in to StayCharted and names its own account.
409partner.no_setup_linkOnly a resold customer that is still active has a set-up link.
409partner.owner_signed_inSomeone has already signed in as this customer’s Owner, so there is no set-up link to send.
409partner.workspace_endingThat workspace is already ending at the end of the paid month.
409partner.workspace_name_takenThis customer already has a workspace called "Finance".
409partner.workspace_primaryThat is the customer’s first workspace. To stop paying for it, cancel the customer.
409review.already_answeredThat answer was already given, or is already in a training run. Reload to see where it is.
429partner.rate_limitedA partner key may make 600 calls a minute. Slow down and retry.
429partner.too_many_refusalsToo many requests with a missing or invalid partner key from this address. Wait a minute.
502partner.billing_unavailableStripe refused the change: …
503api.model_unavailableThis model is temporarily unable to answer. Try again in a few minutes.

Rate limiting

Too many calls. Wait Retry-After seconds.

HeaderMeaning
Retry-AfterOn a 429: seconds until enough allowance has refilled for this request.

Schemas

Error

FieldTypeRequiredNotes
errorstringyesFor a person to read. May be reworded; do not match on it.
codestringyesStable. Branch on this.
reasonsarray of objectnoPresent when the refusal has several causes.

PlanError

An Error caused by the customer's plan, with the limit that refused it.

FieldTypeRequiredNotes
errorstringyes
codestringyes
entitlementstring | nullnoWhich plan limit refused this.
limitinteger | nullno
usedinteger | nullno
resetsAtstring (date-time) | nullnoWhen a monthly allowance refills; null when waiting will not help.

RateLimitError

FieldTypeRequiredNotes
errorstringyes
codestringyes
retryAfterintegernoSeconds to wait.
retryAfterSecondsintegernoThe same, on a customer's own prediction limit.

Partner

FieldTypeRequiredNotes
partnerobjectno
partner.idstring (uuid)no
partner.namestringno
partner.discountPercentnumber | nullno
keyobjectno
key.namestringno
key.prefixstringnoThe key's first characters, to tell your two keys apart.
customersintegernoHow many customers you manage.
billingobjectno
billing.statusstring | nullno
billing.paidUntilstring (date-time) | nullno

AddOns

Add-on ids. vision reads pictures; byom is Your AI Models. What a plan offers is on the pricing page.

NewCustomer

FieldTypeRequiredNotes
namestringyes
planstringyes(one of essentials, business)
addOnsarray of stringnoAdd-on ids. vision reads pictures; byom is Your AI Models. What a plan offers is on the pricing page.
contactEmailstring (email)yesThe customer's contact. For access: api, the address that gets a login link if the customer is ever handed over; nothing is sent before then. For a resold customer (login: true), the account's Owner, mailed at once.
loginbooleannoTrue for a resold customer whose own people sign in to AMT.

Usage

This billing month's counts. Never content.

FieldTypeRequiredNotes
cycleStartstring (date-time) | nullno
predictionsintegerno
predictionsLimitinteger | nullno
picturesintegerno
picturesLimitinteger | nullno

Workspace

FieldTypeRequiredNotes
idstring (uuid)no
namestringno
planstringno
pendingPlanstring | nullnoA downgrade waiting for the end of the paid month.
changesAtstring (date-time) | nullno
endsAtstring (date-time) | nullnoSet when you stopped paying for it.
archivedbooleanno
primarybooleannoThe customer's first workspace.
joinsAtstring (date-time) | nullnoFor a customer that joined you from its own billing: when this workspace moves to your bill, at the end of the month its Owner already paid for. Until then it is not on your bill and its plan cannot be changed (partner.workspace_joining).
addOnsarray of stringno
usageobject | nullnoThis billing month's counts. Never content.
usage.cycleStartstring (date-time) | nullno
usage.predictionsintegerno
usage.predictionsLimitinteger | nullno
usage.picturesintegerno
usage.picturesLimitinteger | nullno

Customer

FieldTypeRequiredNotes
idstring (uuid)yes
namestringyes
contactEmailstringno
accessstringyes(one of api, login)
joinedFromDirectbooleannoThe customer already paid StayCharted itself and asked to be billed through you. Its account was its own before you, so cancelling it moves it back to direct billing rather than closing it.
statusstringyes(one of active, cancelling, cancelled, handed_over)
planstring | nullnoThe first workspace's plan.
pendingPlanstring | nullno
addOnsarray of stringno
endsAtstring (date-time) | nullno
createdAtstring (date-time)no
usageobject | nullnoThis billing month's counts. Never content.
usage.cycleStartstring (date-time) | nullno
usage.predictionsintegerno
usage.predictionsLimitinteger | nullno
usage.picturesintegerno
usage.picturesLimitinteger | nullno
workspacesarray of objectno
workspaces[].idstring (uuid)no
workspaces[].namestringno
workspaces[].planstringno
workspaces[].pendingPlanstring | nullnoA downgrade waiting for the end of the paid month.
workspaces[].changesAtstring (date-time) | nullno
workspaces[].endsAtstring (date-time) | nullnoSet when you stopped paying for it.
workspaces[].archivedbooleanno
workspaces[].primarybooleannoThe customer's first workspace.
workspaces[].joinsAtstring (date-time) | nullnoFor a customer that joined you from its own billing: when this workspace moves to your bill, at the end of the month its Owner already paid for. Until then it is not on your bill and its plan cannot be changed (partner.workspace_joining).
workspaces[].addOnsarray of stringno
workspaces[].usageobject | nullnoThis billing month's counts. Never content.
workspaces[].usage.cycleStartstring (date-time) | nullno
workspaces[].usage.predictionsintegerno
workspaces[].usage.predictionsLimitinteger | nullno
workspaces[].usage.picturesintegerno
workspaces[].usage.picturesLimitinteger | nullno
ownerSignedInAtstring (date-time) | nullnoA resold customer only. When its Owner last signed in; null until they have.

Model

FieldTypeRequiredNotes
idstring (uuid)no
namestringno
descriptionstringno
modalitystringno(one of text, image)
statusstringnoWhere the model is, as the app's Models screen says it.
publishedbooleanno
liveVersionstring (uuid) | nullno
latestVersionstring (uuid) | nullno
accuracynumber | nullnoThe latest version's accuracy on held-out rows, 0 to 1.

ModelDetail

A Model, with every training run.

FieldTypeRequiredNotes
idstring (uuid)no
namestringno
descriptionstringno
modalitystringno(one of text, image)
statusstringno
publishedbooleanno
liveVersionstring (uuid) | nullno
latestVersionstring (uuid) | nullno
accuracynumber | nullno
versionsarray of objectno
versions[].idstring (uuid)no
versions[].statusstringno
versions[].enginestring | nullno
versions[].accuracynumber | nullno
versions[].startedAtstring (date-time) | nullno
versions[].livebooleanno

Columns

The column names, unique, up to 200.

Rows

Rows of values, each with one value per column, in the order of columns. Values are read as text.

Quality

FieldTypeRequiredNotes
versionstring | nullnoPass back with a fix.
usableRowsinteger | nullno
categoryCountinteger | nullno
distributionarray of objectnoRows per category.
duplicateCountintegerno
rareCategoryCountintegerno
verdictstring | nullnoWhether the data is ready to train, as the app's Check data step says.
reasonsarray of objectno
duplicatesarray of objectno

TrainingJob

FieldTypeRequiredNotes
jobIdstring (uuid)no
statusstringnoqueued, running, completed, failed or cancelled.
stagestring | nullnoWhat it is doing now, in words.
failurestring | nullno
reportobject | nullnoThe test on held-out rows, once completed.
report.versionIdstring (uuid)no
report.isLivebooleanno
report.accuracynumber | nullno
report.baselinenumber | nullnoAlways guessing the commonest category.
report.testedOninteger | nullno
report.trainedOninteger | nullno
report.perCategoryarray of objectno
report.perCategory[].categorystringno
report.perCategory[].accuracynumberno
report.perCategory[].correctintegerno
report.perCategory[].totalintegerno

PredictRequest

Exactly one of input or inputs; the server enforces it.

FieldTypeRequiredNotes
inputanynoOne record. A string, an object of the fields the model reads, or for a picture model a link or {"imageBase64":"..."}.
inputsarray of anynoUp to 100 records, each as input takes one (up to 20 pictures). (minItems: 1, maxItems: 100)

Prediction

FieldTypeRequiredNotes
inputanynoThe record, echoed back.
labelstring | nullno
confidencenumber | nullno0 to 1.
needsReviewbooleannoBelow the model's review cutoff; the answer went to the Review Queue.
labelsarray of stringnoFor a model whose answer is a list.
fieldsobjectnoFor a model whose answer has several columns.
errorstringnoA picture that could not be used, as a code.
messagestringno

PredictResponse

FieldTypeRequiredNotes
modelobjectno
model.idstring (uuid)no
model.namestringno
predictionsarray of objectno
predictions[].inputanynoThe record, echoed back.
predictions[].labelstring | nullno
predictions[].confidencenumber | nullno0 to 1.
predictions[].needsReviewbooleannoBelow the model's review cutoff; the answer went to the Review Queue.
predictions[].labelsarray of stringnoFor a model whose answer is a list.
predictions[].fieldsobjectnoFor a model whose answer has several columns.
predictions[].errorstringnoA picture that could not be used, as a code.
predictions[].messagestringno
usageobjectno
usage.predictionsintegernoRecords counted by this request.

ClassifyResponse

FieldTypeRequiredNotes
labelstring | nullno
confidencenumber | nullno
needsReviewbooleanno
labelsarray of stringno
fieldsobjectno
modelIdstring (uuid)no
modelNamestringno
errorstringno
messagestringno

Fill

FieldTypeRequiredNotes
fillIdstring (uuid)no
statusstringnoqueued, running, completed, failed or cancelled.
filledinteger | nullno
totalinteger | nullno
failurestring | nullno
answersarray of objectnoOnce completed, one per row, in the order sent.
answers[].categorystring | nullno
answers[].confidencenumber | nullno
filestringnoOnce completed, the path of the filled file.

ReviewPage

FieldTypeRequiredNotes
openboolean | nullnoWhether the customer's plan has the Review Queue.
cutoffnumber | nullnoAnswers below this confidence come here.
countsobjectno
totalintegerno
categoriesarray of stringno
itemsarray of objectno
items[].groupKeystringnoPass back to answer it.
items[].itemIdstringno
items[].sizeintegernoHow many times this same question was asked.
items[].confidencenumber | nullno
items[].predictedLabelstring | nullno
items[].reviewedLabelstring | nullno
items[].sourcesarray of stringnoThe channels it came from.
items[].textstring | nullno
items[].inputTextstring | nullno
items[].imageHashstring | nullno
items[].firstAtstring (date-time)no
items[].alternativesarray of object | nullnoThe model's next candidates.

Generated from backend/openapi-partner.yaml by scripts/build-api-reference.py. Do not edit by hand.