OpenAPI 4.0.0-candidate72 operations⛁ $0.002378 priced subtotal · 72/72 priced🛡 Hardening A · 96% bounded3 auth schemes

toolfactory API

One typed v4 contract — projected into CRUD · client · UI · cost · docs.

Credit-metered cloud tools (subtitle transcription, and more).

Rendered natively from the v4 document via a semantic IR — the requests-shape, ⛁ cost, signature collisions, effective (composed) shape, and the View-as access projection shown as-is, not flattened to a 3.1 view.

System

Liveness check.

copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/health"
const res = await fetch("https://app.toolfactory.saastemly.com/api/health", {
  method: "GET"
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/health")
data = res.json()
Responses (by name)
200 200 (application/json)
fieldtypenotes
ok*boolean
service*string
example copy
{
  "ok": false,
  "service": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
○ free · liveness

Check if the API is working

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/auth/ok" \
  -H "authorization: Bearer <token>"
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/ok", {
  method: "GET",
  headers: {
    "authorization": "Bearer <token>"
  }
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/auth/ok", headers={"authorization": "Bearer <token>"})
data = res.json()
Responses (by name)
200 200 (application/json) API is working
fieldtypenotes
ok*booleanIndicates if the API is working
example copy
{
  "ok": false
}
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

Displays an error page

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/auth/error" \
  -H "authorization: Bearer <token>"
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/error", {
  method: "GET",
  headers: {
    "authorization": "Bearer <token>"
  }
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/auth/error", headers={"authorization": "Bearer <token>"})
data = res.json()
Responses (by name)
200 200 (text/html) Success
string
example copy
"string"
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

Session

The current Better Auth session, or null when signed out.

copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/auth/get-session"
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/get-session", {
  method: "GET"
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/auth/get-session")
data = res.json()
Responses (by name)
200 200 (application/json)
anyOf
fieldtypenotes
user*object
fieldtypenotes
id*string
emailstring
namestring
null
example copy
{
  "user": {
    "id": "string",
    "email": "string",
    "name": "string"
  }
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
⏱ free · rate-limited

Sign out the current user

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/auth/sign-out" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/sign-out", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({})
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/auth/sign-out", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={})
data = res.json()
body
Request body (application/json)
object
Responses (by name)
200 200 (application/json) Success
fieldtypenotes
successboolean
example copy
{
  "success": false
}
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

Update the current session

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/auth/update-session" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/update-session", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({})
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/auth/update-session", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={})
data = res.json()
body
Request body (application/json)
object
Responses (by name)
200 200 (application/json) Success
fieldtypenotes
sessionSession
example copy
{
  "session": {
    "id": "string",
    "expiresAt": "2026-01-01T00:00:00Z",
    "token": "string",
    "createdAt": "2026-01-01T00:00:00Z",
    "updatedAt": "2026-01-01T00:00:00Z",
    "ipAddress": "string",
    "userAgent": "string",
    "userId": "string"
  }
}
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

List all active sessions for the user

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/auth/list-sessions" \
  -H "authorization: Bearer <token>"
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/list-sessions", {
  method: "GET",
  headers: {
    "authorization": "Bearer <token>"
  }
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/auth/list-sessions", headers={"authorization": "Bearer <token>"})
data = res.json()
Responses (by name)
200 200 (application/json) Success
array of
Session
fieldtypenotes
id*string readOnly
expiresAt*string<date-time>
token*string
createdAt*string<date-time>
updatedAt*string<date-time>
ipAddressstring
userAgentstring
userId*string
example copy
[
  {
    "id": "string",
    "expiresAt": "2026-01-01T00:00:00Z",
    "token": "string",
    "createdAt": "2026-01-01T00:00:00Z",
    "updatedAt": "2026-01-01T00:00:00Z",
    "ipAddress": "string",
    "userAgent": "string",
    "userId": "string"
  }
]
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

Revoke a single session

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/auth/revoke-session" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{"token":"string"}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/revoke-session", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({
    "token": "string"
  })
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/auth/revoke-session", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={
  "token": "string"
})
data = res.json()
body
Request body (application/json)
fieldtypenotes
token*stringThe token to revoke max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
Responses (by name)
200 200 (application/json) Success
fieldtypenotes
status*booleanIndicates if the session was revoked successfully
example copy
{
  "status": false
}
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

Revoke all sessions for the user

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/auth/revoke-sessions" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/revoke-sessions", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({})
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/auth/revoke-sessions", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={})
data = res.json()
body
Request body (application/json)
object
Responses (by name)
200 200 (application/json) Success
fieldtypenotes
status*booleanIndicates if all sessions were revoked successfully
example copy
{
  "status": false
}
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

Revoke all other sessions for the user except the current one

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/auth/revoke-other-sessions" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/revoke-other-sessions", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({})
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/auth/revoke-other-sessions", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={})
data = res.json()
body
Request body (application/json)
object
Responses (by name)
200 200 (application/json) Success
fieldtypenotes
status*booleanIndicates if all other sessions were revoked successfully
example copy
{
  "status": false
}
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

Credits

The signed-in user's credit balance.

🔒 requires sessionCookie
copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/credits" \
  -H "authorization: Bearer <token>"
const res = await fetch("https://app.toolfactory.saastemly.com/api/credits", {
  method: "GET",
  headers: {
    "authorization": "Bearer <token>"
  }
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/credits", headers={"authorization": "Bearer <token>"})
data = res.json()
Responses (by name)
200 200 (application/json)
fieldtypenotes
balance*integer ≥ -9007199254740991≤ 9007199254740991
example copy
{
  "balance": 0
}
401 401 (application/problem+json) Unauthorized
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
403 403 (application/problem+json) Forbidden
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
429 429 (application/problem+json) Too many requests
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
⏱ free · rate-limited

The signed-in user's recent credit ledger (grants, subscription credits, and usage debits), newest first.

🔒 requires sessionCookie
copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/credits/transactions" \
  -H "authorization: Bearer <token>"
const res = await fetch("https://app.toolfactory.saastemly.com/api/credits/transactions", {
  method: "GET",
  headers: {
    "authorization": "Bearer <token>"
  }
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/credits/transactions", headers={"authorization": "Bearer <token>"})
data = res.json()
Responses (by name)
200 200 (application/json)
fieldtypenotes
transactions*array
array of
fieldtypenotes
id*string
delta*integer ≥ -9007199254740991≤ 9007199254740991
reason*string
createdAt*integer ≥ -9007199254740991≤ 9007199254740991
amountCents*anyOf
example copy
{
  "transactions": [
    {
      "id": "string",
      "delta": 0,
      "reason": "string",
      "createdAt": 0,
      "amountCents": 0
    }
  ]
}
401 401 (application/problem+json) Unauthorized
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
403 403 (application/problem+json) Forbidden
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
429 429 (application/problem+json) Too many requests
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
⏱ free · rate-limited

Activity

The signed-in user's Activity, queryable Splunk-style: a bucketed event timeseries + matching ledger events. q filters by type=grant|usage, op (=/!=/~contains), and credits (numeric); range picks the window (1d/7d/30d).

🔒 requires sessionCookie
copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/logs?q=&range=" \
  -H "authorization: Bearer <token>"
const res = await fetch("https://app.toolfactory.saastemly.com/api/logs?q=&range=", {
  method: "GET",
  headers: {
    "authorization": "Bearer <token>"
  }
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/logs?q=&range=", headers={"authorization": "Bearer <token>"})
data = res.json()
Query params
nametypenotesvalue
qstring max len 200pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$ max len 200pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
rangeenum "1d" "7d" "30d"
Responses (by name)
200 200 (application/json)
fieldtypenotes
series*array
array of
fieldtypenotes
t*integer ≥ -9007199254740991≤ 9007199254740991
count*integer ≥ -9007199254740991≤ 9007199254740991
credits*integer ≥ -9007199254740991≤ 9007199254740991
events*array
array of
fieldtypenotes
id*string
at*integer ≥ -9007199254740991≤ 9007199254740991
type*string "grant" "usage"
op*string
credits*integer ≥ -9007199254740991≤ 9007199254740991
keyName*anyOf
range*string "1d" "7d" "30d"
errorstring
example copy
{
  "series": [
    {
      "t": 0,
      "count": 0,
      "credits": 0
    }
  ],
  "events": [
    {
      "id": "string",
      "at": 0,
      "type": "grant",
      "op": "string",
      "credits": 0,
      "keyName": "string"
    }
  ],
  "range": "1d",
  "error": "string"
}
401 401 (application/problem+json) Unauthorized
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
403 403 (application/problem+json) Forbidden
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
429 429 (application/problem+json) Too many requests
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
⏱ free · rate-limited

Billing

Available credit packs (server-authoritative pricing).

copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/billing/packs"
const res = await fetch("https://app.toolfactory.saastemly.com/api/billing/packs", {
  method: "GET"
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/billing/packs")
data = res.json()
Responses (by name)
200 200 (application/json)
fieldtypenotes
packs*array
array of
fieldtypenotes
id*string
credits*integer ≥ -9007199254740991≤ 9007199254740991
priceCents*integer ≥ -9007199254740991≤ 9007199254740991
label*string
example copy
{
  "packs": [
    {
      "id": "string",
      "credits": 0,
      "priceCents": 0,
      "label": "string"
    }
  ]
}
429 429 (application/problem+json) Too many requests
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
⏱ free · rate-limited

Available subscription plans (server-authoritative pricing) — recurring monthly credits at the subsidized rate.

copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/billing/plans"
const res = await fetch("https://app.toolfactory.saastemly.com/api/billing/plans", {
  method: "GET"
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/billing/plans")
data = res.json()
Responses (by name)
200 200 (application/json)
fieldtypenotes
plans*array
array of
fieldtypenotes
id*string
name*string
credits*integer ≥ -9007199254740991≤ 9007199254740991
priceCents*integer ≥ -9007199254740991≤ 9007199254740991
label*string
example copy
{
  "plans": [
    {
      "id": "string",
      "name": "string",
      "credits": 0,
      "priceCents": 0,
      "label": "string"
    }
  ]
}
429 429 (application/problem+json) Too many requests
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
⏱ free · rate-limited

Start a Stripe Checkout for a CUSTOM credit top-up (amount in cents, $1–$1000); returns the hosted checkout URL.

🔒 requires sessionCookie
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/billing/checkout" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{"amountCents":0}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/billing/checkout", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({
    "amountCents": 0
  })
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/billing/checkout", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={
  "amountCents": 0
})
data = res.json()
body
Request body (application/json)
fieldtypenotes
amountCents*integer ≥ 100≤ 100000
Responses (by name)
200 200 (application/json)
fieldtypenotes
url*string
example copy
{
  "url": "string"
}
400 400 (application/problem+json) Bad request
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
401 401 (application/problem+json) Unauthorized
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
403 403 (application/problem+json) Forbidden
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
429 429 (application/problem+json) Too many requests
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
502 502 (application/problem+json) Bad gateway
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
⏱ free · rate-limited

Stripe publishable key for the on-site Payment Element (non-secret, public).

copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/billing/payment-config"
const res = await fetch("https://app.toolfactory.saastemly.com/api/billing/payment-config", {
  method: "GET"
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/billing/payment-config")
data = res.json()
Responses (by name)
200 200 (application/json)
fieldtypenotes
publishableKey*string
example copy
{
  "publishableKey": "string"
}
429 429 (application/problem+json) Too many requests
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
⏱ free · rate-limited

Create a PaymentIntent for an ON-SITE credit top-up (Payment Element + in-page 3DS). Pay X → get Y: returns the client secret + the breakdown (the charge, any sales tax on top, the total, and the credits you'll receive — already net of the processing fee). Credit lands on the webhook.

🔒 requires sessionCookie
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/billing/payment-intent" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{"amountCents":0,"useDefaultCard":false}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/billing/payment-intent", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({
    "amountCents": 0,
    "useDefaultCard": false
  })
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/billing/payment-intent", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={
  "amountCents": 0,
  "useDefaultCard": False
})
data = res.json()
body
Request body (application/json)
fieldtypenotes
amountCents*integer ≥ 100≤ 100000
useDefaultCardboolean
Responses (by name)
200 200 (application/json)
fieldtypenotes
clientSecret*string
breakdown*object
fieldtypenotes
subtotalCents*integer ≥ -9007199254740991≤ 9007199254740991
taxCents*integer ≥ -9007199254740991≤ 9007199254740991
totalCents*integer ≥ -9007199254740991≤ 9007199254740991
credits*integer ≥ -9007199254740991≤ 9007199254740991
example copy
{
  "clientSecret": "string",
  "breakdown": {
    "subtotalCents": 0,
    "taxCents": 0,
    "totalCents": 0,
    "credits": 0
  }
}
400 400 (application/problem+json) Bad request
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
401 401 (application/problem+json) Unauthorized
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
403 403 (application/problem+json) Forbidden
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
429 429 (application/problem+json) Too many requests
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
502 502 (application/problem+json) Bad gateway
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
⏱ free · rate-limited

The signed-in user's CURRENT billing problem (a failed subscription renewal or a declined auto-top-up) needing action, or null when healthy. Drives the persistent alert shown in the billing tab, the sidebar, and the header so the user can fix it (update card / cancel subscription / turn off auto-top-up) from anywhere.

🔒 requires sessionCookie
copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/billing/payment-health" \
  -H "authorization: Bearer <token>"
const res = await fetch("https://app.toolfactory.saastemly.com/api/billing/payment-health", {
  method: "GET",
  headers: {
    "authorization": "Bearer <token>"
  }
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/billing/payment-health", headers={"authorization": "Bearer <token>"})
data = res.json()
Responses (by name)
200 200 (application/json)
fieldtypenotes
alert*anyOf
example copy
{
  "alert": {
    "kind": "subscription_past_due",
    "message": "string"
  }
}
401 401 (application/problem+json) Unauthorized
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
403 403 (application/problem+json) Forbidden
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
429 429 (application/problem+json) Too many requests
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
⏱ free · rate-limited

Quote an on-site top-up of amountCents WITHOUT charging: returns the credits you'll receive (already net of the processing fee), any sales tax added on top, and the total due — the SAME breakdown the PaymentIntent will use, so a one-click Pay matches exactly. Drives the live preview in the Add-credits dialog.

🔒 requires sessionCookie
copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/billing/purchase-quote?amountCents=" \
  -H "authorization: Bearer <token>"
const res = await fetch("https://app.toolfactory.saastemly.com/api/billing/purchase-quote?amountCents=", {
  method: "GET",
  headers: {
    "authorization": "Bearer <token>"
  }
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/billing/purchase-quote?amountCents=", headers={"authorization": "Bearer <token>"})
data = res.json()
Query params
nametypenotesvalue
amountCents*integer ≥ 100≤ 100000 ≥ 100≤ 100000
Responses (by name)
200 200 (application/json)
fieldtypenotes
subtotalCents*integer ≥ -9007199254740991≤ 9007199254740991
taxCents*integer ≥ -9007199254740991≤ 9007199254740991
totalCents*integer ≥ -9007199254740991≤ 9007199254740991
credits*integer ≥ -9007199254740991≤ 9007199254740991
example copy
{
  "subtotalCents": 0,
  "taxCents": 0,
  "totalCents": 0,
  "credits": 0
}
401 401 (application/problem+json) Unauthorized
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
403 403 (application/problem+json) Forbidden
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
429 429 (application/problem+json) Too many requests
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
502 502 (application/problem+json) Bad gateway
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
⏱ free · rate-limited

Create a $0 SetupIntent to vault a card on-site ('Add card'). Returns the client secret the Payment Element confirms.

🔒 requires sessionCookie
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/billing/setup-intent" \
  -H "authorization: Bearer <token>"
const res = await fetch("https://app.toolfactory.saastemly.com/api/billing/setup-intent", {
  method: "POST",
  headers: {
    "authorization": "Bearer <token>"
  }
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/billing/setup-intent", headers={"authorization": "Bearer <token>"})
data = res.json()
Responses (by name)
200 200 (application/json)
fieldtypenotes
clientSecret*string
example copy
{
  "clientSecret": "string"
}
401 401 (application/problem+json) Unauthorized
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
403 403 (application/problem+json) Forbidden
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
429 429 (application/problem+json) Too many requests
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
502 502 (application/problem+json) Bad gateway
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
⏱ free · rate-limited

Subscribe to a plan ONE-CLICK on the saved default card (recurring credits at the subscriber rate). Returns the first invoice's client secret for in-page confirmation; credits land on invoice.paid.

🔒 requires sessionCookie
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/billing/subscribe" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{"planId":"string"}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/billing/subscribe", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({
    "planId": "string"
  })
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/billing/subscribe", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={
  "planId": "string"
})
data = res.json()
body
Request body (application/json)
fieldtypenotes
planId*string max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
Responses (by name)
200 200 (application/json)
fieldtypenotes
clientSecret*string
example copy
{
  "clientSecret": "string"
}
400 400 (application/problem+json) Bad request
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
401 401 (application/problem+json) Unauthorized
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
403 403 (application/problem+json) Forbidden
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
429 429 (application/problem+json) Too many requests
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
502 502 (application/problem+json) Bad gateway
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
⏱ free · rate-limited

The signed-in user's CURRENT subscription (plan id · status · period end · pending cancel · the cycle's paid ceiling — the highest plan price already charged this cycle, below/at which a plan change defers to next renewal), or null when none.

🔒 requires sessionCookie
copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/billing/subscription" \
  -H "authorization: Bearer <token>"
const res = await fetch("https://app.toolfactory.saastemly.com/api/billing/subscription", {
  method: "GET",
  headers: {
    "authorization": "Bearer <token>"
  }
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/billing/subscription", headers={"authorization": "Bearer <token>"})
data = res.json()
Responses (by name)
200 200 (application/json)
fieldtypenotes
subscription*anyOf
example copy
{
  "subscription": {
    "planId": "string",
    "status": "string",
    "currentPeriodEnd": 0,
    "cancelAtPeriodEnd": false,
    "paidCeilingCents": 0
  }
}
401 401 (application/problem+json) Unauthorized
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
403 403 (application/problem+json) Forbidden
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
429 429 (application/problem+json) Too many requests
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
502 502 (application/problem+json) Bad gateway
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
⏱ free · rate-limited

Schedule the subscription to cancel at the period end (cancel:true) or resume it (cancel:false). Access continues until the period ends.

🔒 requires sessionCookie
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/billing/subscription" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{"cancel":false}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/billing/subscription", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({
    "cancel": false
  })
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/billing/subscription", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={
  "cancel": False
})
data = res.json()
body
Request body (application/json)
fieldtypenotes
cancel*boolean
Responses (by name)
200 200 (application/json)
fieldtypenotes
ok*boolean
example copy
{
  "ok": false
}
400 400 (application/problem+json) Bad request
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
401 401 (application/problem+json) Unauthorized
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
403 403 (application/problem+json) Forbidden
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
429 429 (application/problem+json) Too many requests
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
502 502 (application/problem+json) Bad gateway
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
⏱ free · rate-limited

Switch the active subscription to another plan, decided against the cycle's PAID CEILING. ABOVE the ceiling = an immediate prorated upgrade (only the difference ABOVE the ceiling is charged to the saved card now; returns a clientSecret only if that charge needs 3DS) that grants the matching prorated credits. AT OR BELOW the ceiling = a deferred change that takes effect at the next renewal (currentPeriodEnd) with no charge — so changing back to a plan you already paid for this cycle never re-charges.

🔒 requires sessionCookie
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/billing/subscription-plan" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{"planId":"string"}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/billing/subscription-plan", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({
    "planId": "string"
  })
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/billing/subscription-plan", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={
  "planId": "string"
})
data = res.json()
body
Request body (application/json)
fieldtypenotes
planId*string max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
Responses (by name)
200 200 (application/json)
fieldtypenotes
kind*string "upgrade" "downgrade"
clientSecret*anyOf
currentPeriodEnd*integer ≥ -9007199254740991≤ 9007199254740991
example copy
{
  "kind": "upgrade",
  "clientSecret": "string",
  "currentPeriodEnd": 0
}
400 400 (application/problem+json) Bad request
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
401 401 (application/problem+json) Unauthorized
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
403 403 (application/problem+json) Forbidden
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
429 429 (application/problem+json) Too many requests
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
502 502 (application/problem+json) Bad gateway
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
⏱ free · rate-limited

Estimate a credit refund (buyback) WITHOUT executing: pass credits to quote a specific amount (omit to quote the whole balance, capped at it). Returns the credits being quoted, the net cash you'd receive (a flat rate below the cheapest you can buy, minus Stripe fees — always less than you paid), and whether enough refundable payment remains to process it.

🔒 requires sessionCookie
copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/billing/refund-quote?credits=" \
  -H "authorization: Bearer <token>"
const res = await fetch("https://app.toolfactory.saastemly.com/api/billing/refund-quote?credits=", {
  method: "GET",
  headers: {
    "authorization": "Bearer <token>"
  }
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/billing/refund-quote?credits=", headers={"authorization": "Bearer <token>"})
data = res.json()
Query params
nametypenotesvalue
creditsinteger ≤ 9007199254740991> 0 ≤ 9007199254740991> 0
Responses (by name)
200 200 (application/json)
fieldtypenotes
credits*integer ≥ -9007199254740991≤ 9007199254740991
netCents*integer ≥ -9007199254740991≤ 9007199254740991
eligible*boolean
example copy
{
  "credits": 0,
  "netCents": 0,
  "eligible": false
}
401 401 (application/problem+json) Unauthorized
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
403 403 (application/problem+json) Forbidden
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
429 429 (application/problem+json) Too many requests
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
502 502 (application/problem+json) Bad gateway
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
⏱ free · rate-limited

Refund (buy back) credits for cash to your original card(s): pass credits to refund a specific amount (omit to refund the whole balance, capped at it) at a flat rate below the cheapest acquisition price, minus Stripe fees — so you receive less than you paid. Stripe caps each refund at what was charged. Does not cancel a subscription.

🔒 requires sessionCookie
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/billing/refund" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{"credits":0,"requestId":"string"}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/billing/refund", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({
    "credits": 0,
    "requestId": "string"
  })
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/billing/refund", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={
  "credits": 0,
  "requestId": "string"
})
data = res.json()
body
Request body (application/json)
fieldtypenotes
creditsinteger ≤ 9007199254740991> 0
requestIdstring min len 8max len 128pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
Responses (by name)
200 200 (application/json)
fieldtypenotes
refundedCents*integer ≥ -9007199254740991≤ 9007199254740991
creditsRefunded*integer ≥ -9007199254740991≤ 9007199254740991
example copy
{
  "refundedCents": 0,
  "creditsRefunded": 0
}
400 400 (application/problem+json) Bad request
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
401 401 (application/problem+json) Unauthorized
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
403 403 (application/problem+json) Forbidden
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
429 429 (application/problem+json) Too many requests
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
502 502 (application/problem+json) Bad gateway
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
⏱ free · rate-limited

Open the Stripe billing portal to manage/cancel a subscription. Returns the portal URL.

🔒 requires sessionCookie
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/billing/portal" \
  -H "authorization: Bearer <token>"
const res = await fetch("https://app.toolfactory.saastemly.com/api/billing/portal", {
  method: "POST",
  headers: {
    "authorization": "Bearer <token>"
  }
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/billing/portal", headers={"authorization": "Bearer <token>"})
data = res.json()
Responses (by name)
200 200 (application/json)
fieldtypenotes
url*string
example copy
{
  "url": "string"
}
401 401 (application/problem+json) Unauthorized
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
403 403 (application/problem+json) Forbidden
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
404 404 (application/problem+json) Not found
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
429 429 (application/problem+json) Too many requests
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
502 502 (application/problem+json) Bad gateway
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
⏱ free · rate-limited

List the signed-in user's saved cards (each with its billing address); empty until they have a Stripe customer.

🔒 requires sessionCookie
copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/billing/methods" \
  -H "authorization: Bearer <token>"
const res = await fetch("https://app.toolfactory.saastemly.com/api/billing/methods", {
  method: "GET",
  headers: {
    "authorization": "Bearer <token>"
  }
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/billing/methods", headers={"authorization": "Bearer <token>"})
data = res.json()
Responses (by name)
200 200 (application/json)
fieldtypenotes
methods*array
array of
fieldtypenotes
id*string
brand*string
last4*string
expMonth*integer ≥ -9007199254740991≤ 9007199254740991
expYear*integer ≥ -9007199254740991≤ 9007199254740991
name*anyOf
line1*anyOf
line2*anyOf
city*anyOf
region*anyOf
postalCode*anyOf
country*anyOf
isDefault*boolean
example copy
{
  "methods": [
    {
      "id": "string",
      "brand": "string",
      "last4": "string",
      "expMonth": 0,
      "expYear": 0,
      "name": "string",
      "line1": "string",
      "line2": "string",
      "city": "string",
      "region": "string",
      "postalCode": "string",
      "country": "string",
      "isDefault": false
    }
  ]
}
401 401 (application/problem+json) Unauthorized
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
403 403 (application/problem+json) Forbidden
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
429 429 (application/problem+json) Too many requests
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
502 502 (application/problem+json) Bad gateway
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
⏱ free · rate-limited

The signed-in user's auto top-up config (enabled · threshold credits · amount).

🔒 requires sessionCookie
copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/billing/auto-topup" \
  -H "authorization: Bearer <token>"
const res = await fetch("https://app.toolfactory.saastemly.com/api/billing/auto-topup", {
  method: "GET",
  headers: {
    "authorization": "Bearer <token>"
  }
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/billing/auto-topup", headers={"authorization": "Bearer <token>"})
data = res.json()
Responses (by name)
200 200 (application/json)
fieldtypenotes
enabled*boolean
thresholdCredits*integer ≥ -9007199254740991≤ 9007199254740991
amountCents*integer ≥ -9007199254740991≤ 9007199254740991
example copy
{
  "enabled": false,
  "thresholdCredits": 0,
  "amountCents": 0
}
401 401 (application/problem+json) Unauthorized
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
403 403 (application/problem+json) Forbidden
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
429 429 (application/problem+json) Too many requests
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
⏱ free · rate-limited

Enable/disable + configure auto top-up. Enabling requires a saved default card.

🔒 requires sessionCookie
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/billing/auto-topup" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{"enabled":false,"thresholdCredits":0,"amountCents":0}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/billing/auto-topup", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({
    "enabled": false,
    "thresholdCredits": 0,
    "amountCents": 0
  })
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/billing/auto-topup", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={
  "enabled": False,
  "thresholdCredits": 0,
  "amountCents": 0
})
data = res.json()
body
Request body (application/json)
fieldtypenotes
enabled*boolean
thresholdCredits*integer ≥ 1≤ 100000
amountCents*integer ≥ 100≤ 100000
Responses (by name)
200 200 (application/json)
fieldtypenotes
ok*boolean
example copy
{
  "ok": false
}
400 400 (application/problem+json) Bad request
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
401 401 (application/problem+json) Unauthorized
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
403 403 (application/problem+json) Forbidden
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
429 429 (application/problem+json) Too many requests
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
502 502 (application/problem+json) Bad gateway
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
⏱ free · rate-limited

Set one of the user's saved cards as the default for invoices.

🔒 requires sessionCookie
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/billing/methods/default" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{"id":"string"}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/billing/methods/default", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({
    "id": "string"
  })
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/billing/methods/default", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={
  "id": "string"
})
data = res.json()
body
Request body (application/json)
fieldtypenotes
id*string min len 1max len 255pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
Responses (by name)
200 200 (application/json)
fieldtypenotes
ok*boolean
example copy
{
  "ok": false
}
400 400 (application/problem+json) Bad request
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
401 401 (application/problem+json) Unauthorized
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
403 403 (application/problem+json) Forbidden
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
404 404 (application/problem+json) Not found
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
429 429 (application/problem+json) Too many requests
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
502 502 (application/problem+json) Bad gateway
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
⏱ free · rate-limited

Remove (detach) one of the user's saved cards.

🔒 requires sessionCookie
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/billing/methods/delete" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{"id":"string"}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/billing/methods/delete", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({
    "id": "string"
  })
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/billing/methods/delete", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={
  "id": "string"
})
data = res.json()
body
Request body (application/json)
fieldtypenotes
id*string min len 1max len 255pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
Responses (by name)
200 200 (application/json)
fieldtypenotes
ok*boolean
example copy
{
  "ok": false
}
400 400 (application/problem+json) Bad request
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
401 401 (application/problem+json) Unauthorized
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
403 403 (application/problem+json) Forbidden
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
404 404 (application/problem+json) Not found
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
429 429 (application/problem+json) Too many requests
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
502 502 (application/problem+json) Bad gateway
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
⏱ free · rate-limited

API keys

API keys with per-key usage + their place in the delegation tree — name, masked prefix, granted scopes, last used, own + POOLED-subtree credits used vs the key's PAID limit, its % share of the free rate-limit allowance, expiry, revoked state, parent key, and depth. A signed-in user sees all their keys; a caller holding keys:provision sees only the subtree it minted. (Caps are edited via /api/auth/api-key/*; children are minted via provisionKey and revoked via revokeKey.)

🔒 requires sessionCookie
copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/keys" \
  -H "authorization: Bearer <token>"
const res = await fetch("https://app.toolfactory.saastemly.com/api/keys", {
  method: "GET",
  headers: {
    "authorization": "Bearer <token>"
  }
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/keys", headers={"authorization": "Bearer <token>"})
data = res.json()
Responses (by name)
200 200 (application/json)
fieldtypenotes
keys*array
array of
fieldtypenotes
id*string
name*anyOf
start*anyOf
scopes*array
array of string
enabled*boolean
expiresAt*anyOf
lastUsed*anyOf
creditsUsed*integer ≥ -9007199254740991≤ 9007199254740991
subtreeCreditsUsed*integer ≥ -9007199254740991≤ 9007199254740991
creditLimit*anyOf
rateLimitSharePct*anyOf
parentKeyId*anyOf
depth*integer ≥ -9007199254740991≤ 9007199254740991
example copy
{
  "keys": [
    {
      "id": "string",
      "name": "string",
      "start": "string",
      "scopes": [
        "string"
      ],
      "enabled": false,
      "expiresAt": 0,
      "lastUsed": 0,
      "creditsUsed": 0,
      "subtreeCreditsUsed": 0,
      "creditLimit": 0,
      "rateLimitSharePct": 0,
      "parentKeyId": "string",
      "depth": 0
    }
  ]
}
401 401 (application/problem+json) Unauthorized
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
403 403 (application/problem+json) Forbidden
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
429 429 (application/problem+json) Too many requests
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
⏱ free · rate-limited

Mint a CHILD API key. The caller (a signed-in user, or a key/MCP connection holding keys:provision) may request a name, a SUBSET of scopes, and per-key credit/rate caps; the server CLAMPS each to the caller's own effective grant — a child can never out-scope or out-spend an ancestor (caps are min/∩ up the chain, and the child's spend pools into every ancestor's cap). Returns the plaintext key ONCE. Revoking the parent cascade-revokes the child.

🔒 requires sessionCookie
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/keys/provision" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{"name":"string","scopes":[],"creditLimit":0,"rateLimitSharePct":0,"expiresInDays":0,"parentKeyId":"string"}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/keys/provision", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({
    "name": "string",
    "scopes": [],
    "creditLimit": 0,
    "rateLimitSharePct": 0,
    "expiresInDays": 0,
    "parentKeyId": "string"
  })
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/keys/provision", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={
  "name": "string",
  "scopes": [],
  "creditLimit": 0,
  "rateLimitSharePct": 0,
  "expiresInDays": 0,
  "parentKeyId": "string"
})
data = res.json()
body
Request body (application/json)
fieldtypenotes
name*string min len 1max len 120pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
scopes*array = [] max items 20
array of string max len 64pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
creditLimitanyOf
rateLimitSharePctanyOf
expiresInDaysanyOf
parentKeyIdanyOf
Responses (by name)
200 200 (application/json)
fieldtypenotes
key*string
keyId*string
scopes*array
array of string
creditLimit*anyOf
rateLimitSharePct*anyOf
example copy
{
  "key": "string",
  "keyId": "string",
  "scopes": [
    "string"
  ],
  "creditLimit": 0,
  "rateLimitSharePct": 0
}
400 400 (application/problem+json) Bad request
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
401 401 (application/problem+json) Unauthorized
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
403 403 (application/problem+json) Forbidden
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
429 429 (application/problem+json) Too many requests
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
502 502 (application/problem+json) Bad gateway
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
⏱ free · rate-limited

Cascade-revoke an API key — soft-disables the key AND every child key it provisioned, transitively. A signed-in user may revoke any of their own keys; a caller holding keys:provision may revoke only a key inside its own subtree. Returns how many keys were disabled.

🔒 requires sessionCookie
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/keys/revoke" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{"keyId":"string"}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/keys/revoke", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({
    "keyId": "string"
  })
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/keys/revoke", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={
  "keyId": "string"
})
data = res.json()
body
Request body (application/json)
fieldtypenotes
keyId*string min len 1max len 255pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
Responses (by name)
200 200 (application/json)
fieldtypenotes
ok*boolean
revoked*integer ≥ -9007199254740991≤ 9007199254740991
example copy
{
  "ok": false,
  "revoked": 0
}
400 400 (application/problem+json) Bad request
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
401 401 (application/problem+json) Unauthorized
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
403 403 (application/problem+json) Forbidden
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
429 429 (application/problem+json) Too many requests
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
⏱ free · rate-limited

Create a new API key for a user

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/auth/api-key/create" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{"configId":"string","name":"string","expiresIn":0,"prefix":"string","remaining":0,"metadata":{},"refillAmount":0,"refillInterval":0,"rateLimitTimeWindow":0,"rateLimitMax":0,"rateLimitEnabled":false,"permissions":{},"userId":"string","organizationId":"string"}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/api-key/create", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({
    "configId": "string",
    "name": "string",
    "expiresIn": 0,
    "prefix": "string",
    "remaining": 0,
    "metadata": {},
    "refillAmount": 0,
    "refillInterval": 0,
    "rateLimitTimeWindow": 0,
    "rateLimitMax": 0,
    "rateLimitEnabled": false,
    "permissions": {},
    "userId": "string",
    "organizationId": "string"
  })
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/auth/api-key/create", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={
  "configId": "string",
  "name": "string",
  "expiresIn": 0,
  "prefix": "string",
  "remaining": 0,
  "metadata": {},
  "refillAmount": 0,
  "refillInterval": 0,
  "rateLimitTimeWindow": 0,
  "rateLimitMax": 0,
  "rateLimitEnabled": False,
  "permissions": {},
  "userId": "string",
  "organizationId": "string"
})
data = res.json()
body
Request body (application/json)
fieldtypenotes
configIdstringThe configuration ID to use for the API key. If not provided, the default configuration will be used. max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
namestringName of the Api Key max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
expiresInnumber | nullExpiration time of the Api Key in seconds ≥ -1000000000000≤ 1000000000000
prefixstringPrefix of the Api Key max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
remainingnumber | nullRemaining number of requests. Server side only ≥ -1000000000000≤ 1000000000000
metadataany
refillAmountnumberAmount to refill the remaining count of the Api Key. server-only. Eg: 100 ≥ -1000000000000≤ 1000000000000
refillIntervalnumberInterval to refill the Api Key in milliseconds. server-only. Eg: 1000 ≥ -1000000000000≤ 1000000000000
rateLimitTimeWindownumberThe duration in milliseconds where each request is counted. Once the `maxRequests` is reached, the request will be rejected until the `timeWindow` has passed, at which point the `timeWindow` will be reset. server-only. Eg: 1000 ≥ -1000000000000≤ 1000000000000
rateLimitMaxnumberMaximum amount of requests allowed within a window. Once the `maxRequests` is reached, the request will be rejected until the `timeWindow` has passed, at which point the `timeWindow` will be reset. server-only. Eg: 100 ≥ -1000000000000≤ 1000000000000
rateLimitEnabledbooleanWhether the key has rate limiting enabled. server-only. Eg: true
permissionsobjectPermissions of the Api Key.
userIdstringUser Id of the user that the Api Key belongs to. server-only. Eg: "user-id" max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
organizationIdstringOrganization Id of the organization that the Api Key belongs to. Eg: 'org-id' max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
Responses (by name)
200 200 (application/json) API key created successfully
fieldtypenotes
id*stringUnique identifier of the API key
createdAt*string<date-time>Creation timestamp
updatedAt*string<date-time>Last update timestamp
namestring | nullName of the API key
prefixstring | nullPrefix of the API key
startstring | nullStarting characters of the key (if configured)
key*stringThe full API key (only returned on creation)
enabled*booleanWhether the key is enabled
expiresAtstring | nullExpiration timestamp
referenceId*stringID of the reference owning the key
lastRefillAtstring | nullLast refill timestamp
lastRequeststring | nullLast request timestamp
metadataobject | nullMetadata associated with the key
rateLimitMaxnumber | nullMaximum requests in time window
rateLimitTimeWindownumber | nullRate limit time window in milliseconds
remainingnumber | nullRemaining requests
refillAmountnumber | nullAmount to refill
refillIntervalnumber | nullRefill interval in milliseconds
rateLimitEnabled*booleanWhether rate limiting is enabled
requestCount*numberCurrent request count in window
permissionsobject | nullPermissions associated with the key
example copy
{
  "id": "string",
  "createdAt": "2026-01-01T00:00:00Z",
  "updatedAt": "2026-01-01T00:00:00Z",
  "name": "string",
  "prefix": "string",
  "start": "string",
  "key": "string",
  "enabled": false,
  "expiresAt": "2026-01-01T00:00:00Z",
  "referenceId": "string",
  "lastRefillAt": "2026-01-01T00:00:00Z",
  "lastRequest": "2026-01-01T00:00:00Z",
  "metadata": {},
  "rateLimitMax": 0,
  "rateLimitTimeWindow": 0,
  "remaining": 0,
  "refillAmount": 0,
  "refillInterval": 0,
  "rateLimitEnabled": false,
  "requestCount": 0,
  "permissions": {}
}
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

Retrieve an existing API key by ID

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/auth/api-key/get?configId=&id=" \
  -H "authorization: Bearer <token>"
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/api-key/get?configId=&id=", {
  method: "GET",
  headers: {
    "authorization": "Bearer <token>"
  }
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/auth/api-key/get?configId=&id=", headers={"authorization": "Bearer <token>"})
data = res.json()
Query params
nametypenotesvalue
configIdstring max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$The configuration ID to use for the API key lookup. If not provided, the default configuration will be used. max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
idstring max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$The id of the Api Key max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
Responses (by name)
200 200 (application/json) API key retrieved successfully
fieldtypenotes
id*stringID
namestring | nullThe name of the key
startstring | nullShows the first few characters of the API key, including the prefix. This allows you to show those few characters in the UI to make it easier for users to identify the API key.
prefixstring | nullThe API Key prefix. Stored as plain text.
userId*stringThe owner of the user id
refillIntervalnumber | nullThe interval in milliseconds between refills of the `remaining` count. Example: 3600000 // refill every hour (3600000ms = 1h)
refillAmountnumber | nullThe amount to refill
lastRefillAtstring | nullThe last refill date
enabled*booleanSets if key is enabled or disabled = true
rateLimitEnabled*booleanWhether the key has rate limiting enabled
rateLimitTimeWindownumber | nullThe duration in milliseconds
rateLimitMaxnumber | nullMaximum amount of requests allowed within a window
requestCount*numberThe number of requests made within the rate limit time window
remainingnumber | nullRemaining requests (every time api key is used this should updated and should be updated on refill as well)
lastRequeststring | nullWhen last request occurred
expiresAtstring | nullExpiry date of a key
createdAt*string<date-time>created at
updatedAt*string<date-time>updated at
metadataobject | nullExtra metadata about the apiKey
permissionsstring | nullPermissions for the api key (stored as JSON string)
example copy
{
  "id": "string",
  "name": "string",
  "start": "string",
  "prefix": "string",
  "userId": "string",
  "refillInterval": 0,
  "refillAmount": 0,
  "lastRefillAt": "2026-01-01T00:00:00Z",
  "enabled": true,
  "rateLimitEnabled": false,
  "rateLimitTimeWindow": 0,
  "rateLimitMax": 0,
  "requestCount": 0,
  "remaining": 0,
  "lastRequest": "2026-01-01T00:00:00Z",
  "expiresAt": "2026-01-01T00:00:00Z",
  "createdAt": "2026-01-01T00:00:00Z",
  "updatedAt": "2026-01-01T00:00:00Z",
  "metadata": {},
  "permissions": "string"
}
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

Update an existing API key by ID

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/auth/api-key/update" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{"configId":"string","keyId":"string","userId":"string","name":"string","enabled":false,"remaining":0,"refillAmount":0,"refillInterval":0,"metadata":{},"expiresIn":0,"rateLimitEnabled":false,"rateLimitTimeWindow":0,"rateLimitMax":0,"permissions":{}}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/api-key/update", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({
    "configId": "string",
    "keyId": "string",
    "userId": "string",
    "name": "string",
    "enabled": false,
    "remaining": 0,
    "refillAmount": 0,
    "refillInterval": 0,
    "metadata": {},
    "expiresIn": 0,
    "rateLimitEnabled": false,
    "rateLimitTimeWindow": 0,
    "rateLimitMax": 0,
    "permissions": {}
  })
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/auth/api-key/update", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={
  "configId": "string",
  "keyId": "string",
  "userId": "string",
  "name": "string",
  "enabled": False,
  "remaining": 0,
  "refillAmount": 0,
  "refillInterval": 0,
  "metadata": {},
  "expiresIn": 0,
  "rateLimitEnabled": False,
  "rateLimitTimeWindow": 0,
  "rateLimitMax": 0,
  "permissions": {}
})
data = res.json()
body
Request body (application/json)
fieldtypenotes
configIdstringThe configuration ID to use for the API key lookup. If not provided, the default configuration will be used. max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
keyId*stringThe id of the Api Key max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
userIdstringThe id of the user which the api key belongs to. server-only. Eg: "some-user-id" max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
namestringThe name of the key max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
enabledbooleanWhether the Api Key is enabled or not
remainingnumberThe number of remaining requests ≥ -1000000000000≤ 1000000000000
refillAmountnumberThe refill amount ≥ -1000000000000≤ 1000000000000
refillIntervalnumberThe refill interval ≥ -1000000000000≤ 1000000000000
metadataany
expiresInnumber | nullExpiration time of the Api Key in seconds ≥ -1000000000000≤ 1000000000000
rateLimitEnabledbooleanWhether the key has rate limiting enabled.
rateLimitTimeWindownumberThe duration in milliseconds where each request is counted. server-only. Eg: 1000 ≥ -1000000000000≤ 1000000000000
rateLimitMaxnumberMaximum amount of requests allowed within a window. Once the `maxRequests` is reached, the request will be rejected until the `timeWindow` has passed, at which point the `timeWindow` will be reset. server-only. Eg: 100 ≥ -1000000000000≤ 1000000000000
permissionsobject | nullUpdate the permissions on the API Key. server-only.
Responses (by name)
200 200 (application/json) API key updated successfully
fieldtypenotes
id*stringID
namestring | nullThe name of the key
startstring | nullShows the first few characters of the API key, including the prefix. This allows you to show those few characters in the UI to make it easier for users to identify the API key.
prefixstring | nullThe API Key prefix. Stored as plain text.
userId*stringThe owner of the user id
refillIntervalnumber | nullThe interval in milliseconds between refills of the `remaining` count. Example: 3600000 // refill every hour (3600000ms = 1h)
refillAmountnumber | nullThe amount to refill
lastRefillAtstring | nullThe last refill date
enabled*booleanSets if key is enabled or disabled = true
rateLimitEnabled*booleanWhether the key has rate limiting enabled
rateLimitTimeWindownumber | nullThe duration in milliseconds
rateLimitMaxnumber | nullMaximum amount of requests allowed within a window
requestCount*numberThe number of requests made within the rate limit time window
remainingnumber | nullRemaining requests (every time api key is used this should updated and should be updated on refill as well)
lastRequeststring | nullWhen last request occurred
expiresAtstring | nullExpiry date of a key
createdAt*string<date-time>created at
updatedAt*string<date-time>updated at
metadataobject | nullExtra metadata about the apiKey
permissionsstring | nullPermissions for the api key (stored as JSON string)
example copy
{
  "id": "string",
  "name": "string",
  "start": "string",
  "prefix": "string",
  "userId": "string",
  "refillInterval": 0,
  "refillAmount": 0,
  "lastRefillAt": "2026-01-01T00:00:00Z",
  "enabled": true,
  "rateLimitEnabled": false,
  "rateLimitTimeWindow": 0,
  "rateLimitMax": 0,
  "requestCount": 0,
  "remaining": 0,
  "lastRequest": "2026-01-01T00:00:00Z",
  "expiresAt": "2026-01-01T00:00:00Z",
  "createdAt": "2026-01-01T00:00:00Z",
  "updatedAt": "2026-01-01T00:00:00Z",
  "metadata": {},
  "permissions": "string"
}
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

Delete an existing API key

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/auth/api-key/delete" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{"keyId":"string"}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/api-key/delete", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({
    "keyId": "string"
  })
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/auth/api-key/delete", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={
  "keyId": "string"
})
data = res.json()
body
Request body (application/json)
fieldtypenotes
keyId*stringThe id of the API key to delete max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
Responses (by name)
200 200 (application/json) API key deleted successfully
fieldtypenotes
success*booleanIndicates if the API key was successfully deleted
example copy
{
  "success": false
}
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

List all API keys for the authenticated user or for a specific organization

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/auth/api-key/list" \
  -H "authorization: Bearer <token>"
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/api-key/list", {
  method: "GET",
  headers: {
    "authorization": "Bearer <token>"
  }
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/auth/api-key/list", headers={"authorization": "Bearer <token>"})
data = res.json()
Responses (by name)
200 200 (application/json) API keys retrieved successfully
fieldtypenotes
apiKeys*array
array of
fieldtypenotes
id*stringID
namestring | nullThe name of the key
startstring | nullShows the first few characters of the API key, including the prefix. This allows you to show those few characters in the UI to make it easier for users to identify the API key.
prefixstring | nullThe API Key prefix. Stored as plain text.
userId*stringThe owner of the user id
refillIntervalnumber | nullThe interval in milliseconds between refills of the `remaining` count. Example: 3600000 // refill every hour (3600000ms = 1h)
refillAmountnumber | nullThe amount to refill
lastRefillAtstring | nullThe last refill date
enabled*booleanSets if key is enabled or disabled = true
rateLimitEnabled*booleanWhether the key has rate limiting enabled
rateLimitTimeWindownumber | nullThe duration in milliseconds
rateLimitMaxnumber | nullMaximum amount of requests allowed within a window
requestCount*numberThe number of requests made within the rate limit time window
remainingnumber | nullRemaining requests (every time api key is used this should updated and should be updated on refill as well)
lastRequeststring | nullWhen last request occurred
expiresAtstring | nullExpiry date of a key
createdAt*string<date-time>created at
updatedAt*string<date-time>updated at
metadataobject | nullExtra metadata about the apiKey
permissionsstring | nullPermissions for the api key (stored as JSON string)
total*numberTotal number of API keys
limitnumber | nullThe limit used for pagination
offsetnumber | nullThe offset used for pagination
example copy
{
  "apiKeys": [
    {
      "id": "string",
      "name": "string",
      "start": "string",
      "prefix": "string",
      "userId": "string",
      "refillInterval": 0,
      "refillAmount": 0,
      "lastRefillAt": "2026-01-01T00:00:00Z",
      "enabled": true,
      "rateLimitEnabled": false,
      "rateLimitTimeWindow": 0,
      "rateLimitMax": 0,
      "requestCount": 0,
      "remaining": 0,
      "lastRequest": "2026-01-01T00:00:00Z",
      "expiresAt": "2026-01-01T00:00:00Z",
      "createdAt": "2026-01-01T00:00:00Z",
      "updatedAt": "2026-01-01T00:00:00Z",
      "metadata": {},
      "permissions": "string"
    }
  ],
  "total": 0,
  "limit": 0,
  "offset": 0
}
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

MCP

The signed-in user's MCP OAuth connections — the apps they authorized via the MCP authorization flow, with granted tool scopes, the per-connection PAID credit cap + free-tier share, usage, and expiry. (Authorized via /api/auth/mcp/*; this is the usage-joined read.)

🔒 requires sessionCookie
copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/mcp/connections" \
  -H "authorization: Bearer <token>"
const res = await fetch("https://app.toolfactory.saastemly.com/api/mcp/connections", {
  method: "GET",
  headers: {
    "authorization": "Bearer <token>"
  }
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/mcp/connections", headers={"authorization": "Bearer <token>"})
data = res.json()
Responses (by name)
200 200 (application/json)
fieldtypenotes
connections*array
array of
fieldtypenotes
clientId*string
name*anyOf
scopes*array
array of string
creditLimit*anyOf
rateLimitSharePct*anyOf
creditsUsed*integer ≥ -9007199254740991≤ 9007199254740991
expiresAt*anyOf
createdAt*anyOf
example copy
{
  "connections": [
    {
      "clientId": "string",
      "name": "string",
      "scopes": [
        "string"
      ],
      "creditLimit": 0,
      "rateLimitSharePct": 0,
      "creditsUsed": 0,
      "expiresAt": 0,
      "createdAt": 0
    }
  ]
}
401 401 (application/problem+json) Unauthorized
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
403 403 (application/problem+json) Forbidden
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
429 429 (application/problem+json) Too many requests
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
⏱ free · rate-limited

Set an MCP connection's per-connection credit cap / free-tier share, and/or narrow its granted scopes (the live token updates immediately).

🔒 requires sessionCookie
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/mcp/connections/update" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{"clientId":"string","creditLimit":0,"rateLimitSharePct":0,"scopes":["string"]}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/mcp/connections/update", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({
    "clientId": "string",
    "creditLimit": 0,
    "rateLimitSharePct": 0,
    "scopes": [
      "string"
    ]
  })
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/mcp/connections/update", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={
  "clientId": "string",
  "creditLimit": 0,
  "rateLimitSharePct": 0,
  "scopes": [
    "string"
  ]
})
data = res.json()
body
Request body (application/json)
fieldtypenotes
clientId*string min len 1max len 255pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
creditLimitanyOf
rateLimitSharePctanyOf
scopesarray max items 20
array of string max len 64pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
Responses (by name)
200 200 (application/json)
fieldtypenotes
ok*boolean
example copy
{
  "ok": false
}
400 400 (application/problem+json) Bad request
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
401 401 (application/problem+json) Unauthorized
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
403 403 (application/problem+json) Forbidden
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
429 429 (application/problem+json) Too many requests
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
⏱ free · rate-limited

Revoke an MCP connection — its token stops working immediately.

🔒 requires sessionCookie
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/mcp/connections/revoke" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{"clientId":"string"}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/mcp/connections/revoke", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({
    "clientId": "string"
  })
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/mcp/connections/revoke", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={
  "clientId": "string"
})
data = res.json()
body
Request body (application/json)
fieldtypenotes
clientId*string min len 1max len 255pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
Responses (by name)
200 200 (application/json)
fieldtypenotes
ok*boolean
example copy
{
  "ok": false
}
400 400 (application/problem+json) Bad request
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
401 401 (application/problem+json) Unauthorized
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
403 403 (application/problem+json) Forbidden
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
429 429 (application/problem+json) Too many requests
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
⏱ free · rate-limited

Better Auth — .well-known oauth-authorization-server (MCP)

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/auth/.well-known/oauth-authorization-server" \
  -H "authorization: Bearer <token>"
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/.well-known/oauth-authorization-server", {
  method: "GET",
  headers: {
    "authorization": "Bearer <token>"
  }
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/auth/.well-known/oauth-authorization-server", headers={"authorization": "Bearer <token>"})
data = res.json()
Responses (by name)
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

Better Auth — .well-known oauth-protected-resource (MCP)

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/auth/.well-known/oauth-protected-resource" \
  -H "authorization: Bearer <token>"
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/.well-known/oauth-protected-resource", {
  method: "GET",
  headers: {
    "authorization": "Bearer <token>"
  }
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/auth/.well-known/oauth-protected-resource", headers={"authorization": "Bearer <token>"})
data = res.json()
Responses (by name)
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

Authorize an OAuth2 request using MCP

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/auth/mcp/authorize" \
  -H "authorization: Bearer <token>"
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/mcp/authorize", {
  method: "GET",
  headers: {
    "authorization": "Bearer <token>"
  }
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/auth/mcp/authorize", headers={"authorization": "Bearer <token>"})
data = res.json()
Responses (by name)
200 200 (application/json) Authorization response generated successfully
object
example copy
{}
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

Better Auth — mcp token (MCP)

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/auth/mcp/token" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/mcp/token", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({})
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/auth/mcp/token", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={})
data = res.json()
body
Request body (application/json)
object
Responses (by name)
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

Register an OAuth2 application

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/auth/mcp/register" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{"redirect_uris":["string"],"token_endpoint_auth_method":"none","grant_types":["authorization_code"],"response_types":["code"],"client_name":"string","client_uri":"string","logo_uri":"string","scope":"string","contacts":["string"],"tos_uri":"string","policy_uri":"string","jwks_uri":"string","jwks":{},"metadata":{},"software_id":"string","software_version":"string","software_statement":"string"}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/mcp/register", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({
    "redirect_uris": [
      "string"
    ],
    "token_endpoint_auth_method": "none",
    "grant_types": [
      "authorization_code"
    ],
    "response_types": [
      "code"
    ],
    "client_name": "string",
    "client_uri": "string",
    "logo_uri": "string",
    "scope": "string",
    "contacts": [
      "string"
    ],
    "tos_uri": "string",
    "policy_uri": "string",
    "jwks_uri": "string",
    "jwks": {},
    "metadata": {},
    "software_id": "string",
    "software_version": "string",
    "software_statement": "string"
  })
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/auth/mcp/register", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={
  "redirect_uris": [
    "string"
  ],
  "token_endpoint_auth_method": "none",
  "grant_types": [
    "authorization_code"
  ],
  "response_types": [
    "code"
  ],
  "client_name": "string",
  "client_uri": "string",
  "logo_uri": "string",
  "scope": "string",
  "contacts": [
    "string"
  ],
  "tos_uri": "string",
  "policy_uri": "string",
  "jwks_uri": "string",
  "jwks": {},
  "metadata": {},
  "software_id": "string",
  "software_version": "string",
  "software_statement": "string"
})
data = res.json()
body
Request body (application/json)
fieldtypenotes
redirect_uris*array max items 1000
array of string max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
token_endpoint_auth_methodstring "none" "client_secret_basic" "client_secret_post"
grant_typesarray max items 1000
array of enum "authorization_code" "implicit" "password" "client_credentials" "refresh_token" "urn:ietf:params:oauth:grant-type:jwt-bearer" "urn:ietf:params:oauth:grant-type:saml2-bearer"
response_typesarray max items 1000
array of enum "code" "token"
client_namestring max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
client_uristring max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
logo_uristring max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
scopestring max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
contactsarray max items 1000
array of string max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
tos_uristring max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
policy_uristring max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
jwks_uristring max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
jwksobject
metadataobject
software_idstring max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
software_versionstring max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
software_statementstring max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
Responses (by name)
200 200 (application/json) OAuth2 application registered successfully
fieldtypenotes
name*stringName of the OAuth2 application
iconstring | nullIcon URL for the application
metadataobject | nullAdditional metadata for the application
clientId*stringUnique identifier for the client
clientSecretstringSecret key for the client. Not included for public clients.
redirectUrls*arrayList of allowed redirect URLs
array of string<uri>
type*stringType of the client "web" "public"
authenticationScheme*stringAuthentication scheme used by the client "client_secret" "none"
disabled*booleanWhether the client is disabled false
userIdstring | nullID of the user who registered the client, null if registered anonymously
createdAt*string<date-time>Creation timestamp
updatedAt*string<date-time>Last update timestamp
example copy
{
  "name": "string",
  "icon": "string",
  "metadata": {},
  "clientId": "string",
  "clientSecret": "string",
  "redirectUrls": [
    "string"
  ],
  "type": "web",
  "authenticationScheme": "client_secret",
  "disabled": false,
  "userId": "string",
  "createdAt": "2026-01-01T00:00:00Z",
  "updatedAt": "2026-01-01T00:00:00Z"
}
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

Better Auth — mcp get-session (MCP)

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/auth/mcp/get-session" \
  -H "authorization: Bearer <token>"
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/mcp/get-session", {
  method: "GET",
  headers: {
    "authorization": "Bearer <token>"
  }
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/auth/mcp/get-session", headers={"authorization": "Bearer <token>"})
data = res.json()
Responses (by name)
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

Tools

Transcribe audio/video to subtitles (SRT/VTT) via Whisper on Workers AI. Auth + credit-gated.

🔒 requires sessionCookie
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/transcribe" \
  -H "content-type: multipart/form-data" \
  -H "authorization: Bearer <token>" \
  -d '{"file":"string","format":"srt"}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/transcribe", {
  method: "POST",
  headers: {
    "content-type": "multipart/form-data",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({
    "file": "string",
    "format": "srt"
  })
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/transcribe", headers={"content-type": "multipart/form-data", "authorization": "Bearer <token>"}, json={
  "file": "string",
  "format": "srt"
})
data = res.json()
body
Request body (multipart/form-data)
fieldtypenotes
file*stringthe audio/video file (multipart upload) max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
formatstringoutput subtitle format (default srt) "srt" "vtt" "ass" "ssa" "sbv" "lrc"
Responses (by name)
200 200 (text/plain)
string
example copy
"string"
400 400 (application/problem+json) Bad request
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
401 401 (application/problem+json) Unauthorized
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
402 402 (application/problem+json) Payment required
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
403 403 (application/problem+json) Forbidden
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
413 413 (application/problem+json) Error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
502 502 (application/problem+json) Bad gateway
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
💳 paid · credits

Bulk-convert subtitle files between formats (srt/vtt/ass/ssa/sbv/smi/lrc/sub). Auth + credit-gated (1 credit per converted file). Per-item + idempotent on a batch requestId + per-file id (a retry never double-charges).

🔒 requires sessionCookie
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/subtitle/convert" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{"to":"srt","from":"srt","requestId":"string","files":[{"id":"string","name":"string","content":"string"}]}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/subtitle/convert", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({
    "to": "srt",
    "from": "srt",
    "requestId": "string",
    "files": [
      {
        "id": "string",
        "name": "string",
        "content": "string"
      }
    ]
  })
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/subtitle/convert", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={
  "to": "srt",
  "from": "srt",
  "requestId": "string",
  "files": [
    {
      "id": "string",
      "name": "string",
      "content": "string"
    }
  ]
})
data = res.json()
body
Request body (application/json)
fieldtypenotes
to*string "srt" "vtt" "ass" "ssa" "sbv" "lrc"
fromstring "srt" "vtt" "ass" "ssa" "sbv" "lrc"
requestIdstring min len 8max len 128pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
files*array min items 1max items 20
array of
fieldtypenotes
idstring min len 8max len 128pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
name*string min len 1max len 255pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
content*string min len 1max len 1000000pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
Responses (by name)
200 200 (application/json)
fieldtypenotes
results*arraythe per-file outcome, in input order
array of
fieldtypenotes
idstringthe per-file id echoed from the request (for mapping results back)
name*string
status*string "done" "error" "skipped"
contentstringthe converted subtitle text (present when status=done)
errorstringwhy a file was not converted/charged (parse detail, out_of_credits, or key_limit)
charged*integercredits debited for THIS file (0 on error/skip/replay) ≥ -9007199254740991≤ 9007199254740991
totalCharged*integercredits debited across the batch ≥ -9007199254740991≤ 9007199254740991
replayed*booleantrue when the whole batch was a retry that moved no new money
example copy
{
  "results": [
    {
      "id": "string",
      "name": "string",
      "status": "done",
      "content": "string",
      "error": "string",
      "charged": 0
    }
  ],
  "totalCharged": 0,
  "replayed": false
}
400 400 (application/problem+json) Bad request
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
401 401 (application/problem+json) Unauthorized
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
402 402 (application/problem+json) Payment required
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
403 403 (application/problem+json) Forbidden
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
500 500 (application/problem+json) Internal server error
ProblemDetails
fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated
example copy
{
  "type": "about:blank",
  "title": "string",
  "status": 0,
  "detail": "string",
  "instance": "string",
  "errors": {},
  "error": "string"
}
💳 paid · credits

Sign in

Sign in with a social provider

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/auth/sign-in/social" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{"callbackURL":"string","newUserCallbackURL":"string","errorCallbackURL":"string","provider":"apple","disableRedirect":false,"idToken":{"token":"string","nonce":"string","accessToken":"string","refreshToken":"string","expiresAt":0,"user":{"name":{"firstName":"string","lastName":"string"},"email":"string"}},"scopes":["string"],"requestSignUp":false,"loginHint":"string","additionalData":{}}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/sign-in/social", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({
    "callbackURL": "string",
    "newUserCallbackURL": "string",
    "errorCallbackURL": "string",
    "provider": "apple",
    "disableRedirect": false,
    "idToken": {
      "token": "string",
      "nonce": "string",
      "accessToken": "string",
      "refreshToken": "string",
      "expiresAt": 0,
      "user": {
        "name": {
          "firstName": "string",
          "lastName": "string"
        },
        "email": "string"
      }
    },
    "scopes": [
      "string"
    ],
    "requestSignUp": false,
    "loginHint": "string",
    "additionalData": {}
  })
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/auth/sign-in/social", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={
  "callbackURL": "string",
  "newUserCallbackURL": "string",
  "errorCallbackURL": "string",
  "provider": "apple",
  "disableRedirect": False,
  "idToken": {
    "token": "string",
    "nonce": "string",
    "accessToken": "string",
    "refreshToken": "string",
    "expiresAt": 0,
    "user": {
      "name": {
        "firstName": "string",
        "lastName": "string"
      },
      "email": "string"
    }
  },
  "scopes": [
    "string"
  ],
  "requestSignUp": False,
  "loginHint": "string",
  "additionalData": {}
})
data = res.json()
body
Request body (application/json)
fieldtypenotes
callbackURLstringCallback URL to redirect to after the user has signed in max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
newUserCallbackURLstring max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
errorCallbackURLstringCallback URL to redirect to if an error happens max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
provider*anyOf
disableRedirectbooleanDisable automatic redirection to the provider. Useful for handling the redirection yourself
idTokenobject
fieldtypenotes
token*stringID token from the provider max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
noncestringNonce used to generate the token max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
accessTokenstringAccess token from the provider max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
refreshTokenstringRefresh token from the provider max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
expiresAtnumberExpiry date of the token ≥ -1000000000000≤ 1000000000000
userobjectThe user object from the provider. Only available for some providers like Apple.
fieldtypenotes
nameobject
fieldtypenotes
firstNamestring max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
lastNamestring max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
emailstring max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
scopesarrayArray of scopes to request from the provider. This will override the default scopes passed. max items 1000
array of string max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
requestSignUpbooleanExplicitly request sign-up. Useful when disableImplicitSignUp is true for this provider
loginHintstringThe login hint to use for the authorization code request max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
additionalDataobject
Responses (by name)
200 200 (application/json) Success - Returns session details (idToken branch) or an authorize URL (redirect branch)
fieldtypenotes
tokenstring
userUser
urlstring
redirect*boolean
example copy
{
  "token": "string",
  "user": {
    "id": "string",
    "name": "string",
    "email": "string",
    "emailVerified": false,
    "image": "string",
    "createdAt": "2026-01-01T00:00:00Z",
    "updatedAt": "2026-01-01T00:00:00Z"
  },
  "url": "string",
  "redirect": false
}
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

Better Auth — callback {id} (Sign in)

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/auth/callback/:id" \
  -H "authorization: Bearer <token>"
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/callback/:id", {
  method: "GET",
  headers: {
    "authorization": "Bearer <token>"
  }
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/auth/callback/:id", headers={"authorization": "Bearer <token>"})
data = res.json()
Path params
nametypenotesvalue
id*string max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$ max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
Responses (by name)
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

Better Auth — callback {id} (Sign in)

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/auth/callback/:id" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{"code":"string","error":"string","device_id":"string","error_description":"string","state":"string","user":"string"}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/callback/:id", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({
    "code": "string",
    "error": "string",
    "device_id": "string",
    "error_description": "string",
    "state": "string",
    "user": "string"
  })
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/auth/callback/:id", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={
  "code": "string",
  "error": "string",
  "device_id": "string",
  "error_description": "string",
  "state": "string",
  "user": "string"
})
data = res.json()
body
Path params
nametypenotesvalue
id*string max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$ max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
Request body (application/json)
fieldtypenotes
codestring max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
errorstring max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
device_idstring max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
error_descriptionstring max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
statestring max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
userstring max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
Responses (by name)
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

List all accounts linked to the user

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/auth/list-accounts" \
  -H "authorization: Bearer <token>"
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/list-accounts", {
  method: "GET",
  headers: {
    "authorization": "Bearer <token>"
  }
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/auth/list-accounts", headers={"authorization": "Bearer <token>"})
data = res.json()
Responses (by name)
200 200 (application/json) Success
array of
fieldtypenotes
id*string
providerId*string
createdAt*string<date-time>
updatedAt*string<date-time>
accountId*string
userId*string
scopes*array
array of string
example copy
[
  {
    "id": "string",
    "providerId": "string",
    "createdAt": "2026-01-01T00:00:00Z",
    "updatedAt": "2026-01-01T00:00:00Z",
    "accountId": "string",
    "userId": "string",
    "scopes": [
      "string"
    ]
  }
]
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

Refresh the access token using a refresh token

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/auth/refresh-token" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{"providerId":"string","accountId":"string","userId":"string"}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/refresh-token", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({
    "providerId": "string",
    "accountId": "string",
    "userId": "string"
  })
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/auth/refresh-token", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={
  "providerId": "string",
  "accountId": "string",
  "userId": "string"
})
data = res.json()
body
Request body (application/json)
fieldtypenotes
providerId*stringThe provider ID for the OAuth provider max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
accountIdstringThe account ID associated with the refresh token max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
userIdstringThe user ID associated with the account max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
Responses (by name)
200 200 (application/json) Access token refreshed successfully
fieldtypenotes
tokenTypestring
idTokenstring
accessTokenstring
refreshTokenstring
accessTokenExpiresAtstring<date-time>
refreshTokenExpiresAtstring<date-time>
example copy
{
  "tokenType": "string",
  "idToken": "string",
  "accessToken": "string",
  "refreshToken": "string",
  "accessTokenExpiresAt": "2026-01-01T00:00:00Z",
  "refreshTokenExpiresAt": "2026-01-01T00:00:00Z"
}
400 400 Invalid refresh token or provider configuration
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

Get a valid access token, doing a refresh if needed

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/auth/get-access-token" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{"providerId":"string","accountId":"string","userId":"string"}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/get-access-token", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({
    "providerId": "string",
    "accountId": "string",
    "userId": "string"
  })
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/auth/get-access-token", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={
  "providerId": "string",
  "accountId": "string",
  "userId": "string"
})
data = res.json()
body
Request body (application/json)
fieldtypenotes
providerId*stringThe provider ID for the OAuth provider max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
accountIdstringThe account ID associated with the refresh token max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
userIdstringThe user ID associated with the account max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
Responses (by name)
200 200 (application/json) A Valid access token
fieldtypenotes
tokenTypestring
idTokenstring
accessTokenstring
accessTokenExpiresAtstring<date-time>
example copy
{
  "tokenType": "string",
  "idToken": "string",
  "accessToken": "string",
  "accessTokenExpiresAt": "2026-01-01T00:00:00Z"
}
400 400 Invalid refresh token or provider configuration
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

Get the account info provided by the provider

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/auth/account-info" \
  -H "authorization: Bearer <token>"
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/account-info", {
  method: "GET",
  headers: {
    "authorization": "Bearer <token>"
  }
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/auth/account-info", headers={"authorization": "Bearer <token>"})
data = res.json()
Responses (by name)
200 200 (application/json) Success
fieldtypenotes
user*object
fieldtypenotes
id*string
namestring
emailstring
imagestring
emailVerified*boolean
data*object
object
example copy
{
  "user": {
    "id": "string",
    "name": "string",
    "email": "string",
    "image": "string",
    "emailVerified": false
  },
  "data": {}
}
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

Account

Update the current user

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/auth/update-user" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{"name":"string","image":"string"}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/update-user", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({
    "name": "string",
    "image": "string"
  })
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/auth/update-user", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={
  "name": "string",
  "image": "string"
})
data = res.json()
body
Request body (application/json)
fieldtypenotes
namestringThe name of the user max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
imagestring | nullThe image of the user max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
Responses (by name)
200 200 (application/json) Success
fieldtypenotes
userUser
example copy
{
  "user": {
    "id": "string",
    "name": "string",
    "email": "string",
    "emailVerified": false,
    "image": "string",
    "createdAt": "2026-01-01T00:00:00Z",
    "updatedAt": "2026-01-01T00:00:00Z"
  }
}
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

Delete the user

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/auth/delete-user" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{"callbackURL":"string","password":"string","token":"string"}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/delete-user", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({
    "callbackURL": "string",
    "password": "string",
    "token": "string"
  })
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/auth/delete-user", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={
  "callbackURL": "string",
  "password": "string",
  "token": "string"
})
data = res.json()
body
Request body (application/json)
fieldtypenotes
callbackURLstringThe callback URL to redirect to after the user is deleted max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
passwordstringThe user's password. Required if session is not fresh max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
tokenstringThe deletion verification token max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
Responses (by name)
200 200 (application/json) User deletion processed successfully
fieldtypenotes
success*booleanIndicates if the operation was successful
message*stringStatus message of the deletion process "User deleted" "Verification email sent"
example copy
{
  "success": false,
  "message": "User deleted"
}
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

Callback to complete user deletion with verification token

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/auth/delete-user/callback?token=&callbackURL=" \
  -H "authorization: Bearer <token>"
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/delete-user/callback?token=&callbackURL=", {
  method: "GET",
  headers: {
    "authorization": "Bearer <token>"
  }
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/auth/delete-user/callback?token=&callbackURL=", headers={"authorization": "Bearer <token>"})
data = res.json()
Query params
nametypenotesvalue
tokenstring max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$The token to verify the deletion request max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
callbackURLstring max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$The URL to redirect to after deletion max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
Responses (by name)
200 200 (application/json) User successfully deleted
fieldtypenotes
success*booleanIndicates if the deletion was successful
message*stringConfirmation message "User deleted"
example copy
{
  "success": false,
  "message": "User deleted"
}
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

Passkey

Generate registration options for a new passkey

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/auth/passkey/generate-register-options?authenticatorAttachment=&name=&context=" \
  -H "authorization: Bearer <token>"
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/passkey/generate-register-options?authenticatorAttachment=&name=&context=", {
  method: "GET",
  headers: {
    "authorization": "Bearer <token>"
  }
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/auth/passkey/generate-register-options?authenticatorAttachment=&name=&context=", headers={"authorization": "Bearer <token>"})
data = res.json()
Query params
nametypenotesvalue
authenticatorAttachmentstring max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$ max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
namestring max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$ max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
contextstring max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$ max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
Responses (by name)
200 200 (application/json) Success
fieldtypenotes
challengestring
rpobject
fieldtypenotes
namestring
idstring
userobject
fieldtypenotes
idstring
namestring
displayNamestring
pubKeyCredParamsarray
array of
fieldtypenotes
typestring
algnumber
timeoutnumber
excludeCredentialsarray
array of
fieldtypenotes
idstring
typestring
transportsarray
array of string
authenticatorSelectionobject
fieldtypenotes
authenticatorAttachmentstring
requireResidentKeyboolean
userVerificationstring
attestationstring
extensionsobject
example copy
{
  "challenge": "string",
  "rp": {
    "name": "string",
    "id": "string"
  },
  "user": {
    "id": "string",
    "name": "string",
    "displayName": "string"
  },
  "pubKeyCredParams": [
    {
      "type": "string",
      "alg": 0
    }
  ],
  "timeout": 0,
  "excludeCredentials": [
    {
      "id": "string",
      "type": "string",
      "transports": [
        "string"
      ]
    }
  ],
  "authenticatorSelection": {
    "authenticatorAttachment": "string",
    "requireResidentKey": false,
    "userVerification": "string"
  },
  "attestation": "string",
  "extensions": {}
}
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

Generate authentication options for a passkey

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/auth/passkey/generate-authenticate-options" \
  -H "authorization: Bearer <token>"
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/passkey/generate-authenticate-options", {
  method: "GET",
  headers: {
    "authorization": "Bearer <token>"
  }
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/auth/passkey/generate-authenticate-options", headers={"authorization": "Bearer <token>"})
data = res.json()
Responses (by name)
200 200 (application/json) Success
fieldtypenotes
challengestring
rpobject
fieldtypenotes
namestring
idstring
userobject
fieldtypenotes
idstring
namestring
displayNamestring
timeoutnumber
allowCredentialsarray
array of
fieldtypenotes
idstring
typestring
transportsarray
array of string
userVerificationstring
authenticatorSelectionobject
fieldtypenotes
authenticatorAttachmentstring
requireResidentKeyboolean
userVerificationstring
extensionsobject
example copy
{
  "challenge": "string",
  "rp": {
    "name": "string",
    "id": "string"
  },
  "user": {
    "id": "string",
    "name": "string",
    "displayName": "string"
  },
  "timeout": 0,
  "allowCredentials": [
    {
      "id": "string",
      "type": "string",
      "transports": [
        "string"
      ]
    }
  ],
  "userVerification": "string",
  "authenticatorSelection": {
    "authenticatorAttachment": "string",
    "requireResidentKey": false,
    "userVerification": "string"
  },
  "extensions": {}
}
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

Verify registration of a new passkey

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/auth/passkey/verify-registration" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{"name":"string","response":{"id":"string","rawId":"string","type":"public-key","authenticatorAttachment":"platform","clientExtensionResults":{},"response":{"clientDataJSON":"string","attestationObject":"string","transports":["string"],"authenticatorData":"string","publicKey":"string","publicKeyAlgorithm":0}}}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/passkey/verify-registration", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({
    "name": "string",
    "response": {
      "id": "string",
      "rawId": "string",
      "type": "public-key",
      "authenticatorAttachment": "platform",
      "clientExtensionResults": {},
      "response": {
        "clientDataJSON": "string",
        "attestationObject": "string",
        "transports": [
          "string"
        ],
        "authenticatorData": "string",
        "publicKey": "string",
        "publicKeyAlgorithm": 0
      }
    }
  })
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/auth/passkey/verify-registration", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={
  "name": "string",
  "response": {
    "id": "string",
    "rawId": "string",
    "type": "public-key",
    "authenticatorAttachment": "platform",
    "clientExtensionResults": {},
    "response": {
      "clientDataJSON": "string",
      "attestationObject": "string",
      "transports": [
        "string"
      ],
      "authenticatorData": "string",
      "publicKey": "string",
      "publicKeyAlgorithm": 0
    }
  }
})
data = res.json()
body
Request body (application/json)
fieldtypenotes
namestring max len 256pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
response*object
fieldtypenotes
id*string max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
rawId*string max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
type*string
authenticatorAttachmentstring "platform" "cross-platform"
clientExtensionResultsobject
response*object
fieldtypenotes
clientDataJSON*string max len 8192pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
attestationObject*string max len 32768pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
transportsarray max items 8
array of string max len 32pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
authenticatorDatastring max len 8192pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
publicKeystring max len 4096pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
publicKeyAlgorithminteger ≥ -65535≤ 65535
Responses (by name)
200 200 (application/json) Success
Passkey
fieldtypenotes
id*string readOnly
namestring
publicKey*string
userId*string
credentialID*string
counter*number
deviceType*string
backedUp*boolean
transportsstring
createdAtstring<date-time>
aaguidstring
example copy
{
  "id": "string",
  "name": "string",
  "publicKey": "string",
  "userId": "string",
  "credentialID": "string",
  "counter": 0,
  "deviceType": "string",
  "backedUp": false,
  "transports": "string",
  "createdAt": "2026-01-01T00:00:00Z",
  "aaguid": "string"
}
400 400 Bad request
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

Verify authentication of a passkey

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/auth/passkey/verify-authentication" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{"response":{"id":"string","rawId":"string","type":"public-key","authenticatorAttachment":"platform","clientExtensionResults":{},"response":{"clientDataJSON":"string","authenticatorData":"string","signature":"string","userHandle":"string"}}}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/passkey/verify-authentication", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({
    "response": {
      "id": "string",
      "rawId": "string",
      "type": "public-key",
      "authenticatorAttachment": "platform",
      "clientExtensionResults": {},
      "response": {
        "clientDataJSON": "string",
        "authenticatorData": "string",
        "signature": "string",
        "userHandle": "string"
      }
    }
  })
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/auth/passkey/verify-authentication", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={
  "response": {
    "id": "string",
    "rawId": "string",
    "type": "public-key",
    "authenticatorAttachment": "platform",
    "clientExtensionResults": {},
    "response": {
      "clientDataJSON": "string",
      "authenticatorData": "string",
      "signature": "string",
      "userHandle": "string"
    }
  }
})
data = res.json()
body
Request body (application/json)
fieldtypenotes
response*object
fieldtypenotes
id*string max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
rawId*string max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
type*string
authenticatorAttachmentstring "platform" "cross-platform"
clientExtensionResultsobject
response*object
fieldtypenotes
clientDataJSON*string max len 8192pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
authenticatorData*string max len 8192pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
signature*string max len 4096pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
userHandlestring max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
Responses (by name)
200 200 (application/json) Success
fieldtypenotes
sessionSession
userUser
example copy
{
  "session": {
    "id": "string",
    "expiresAt": "2026-01-01T00:00:00Z",
    "token": "string",
    "createdAt": "2026-01-01T00:00:00Z",
    "updatedAt": "2026-01-01T00:00:00Z",
    "ipAddress": "string",
    "userAgent": "string",
    "userId": "string"
  },
  "user": {
    "id": "string",
    "name": "string",
    "email": "string",
    "emailVerified": false,
    "image": "string",
    "createdAt": "2026-01-01T00:00:00Z",
    "updatedAt": "2026-01-01T00:00:00Z"
  }
}
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

List all passkeys for the authenticated user

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X GET "https://app.toolfactory.saastemly.com/api/auth/passkey/list-user-passkeys" \
  -H "authorization: Bearer <token>"
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/passkey/list-user-passkeys", {
  method: "GET",
  headers: {
    "authorization": "Bearer <token>"
  }
});
const data = await res.json();
import requests
res = requests.get("https://app.toolfactory.saastemly.com/api/auth/passkey/list-user-passkeys", headers={"authorization": "Bearer <token>"})
data = res.json()
Responses (by name)
200 200 (application/json) Passkeys retrieved successfully
array of
Passkey
fieldtypenotes
id*string readOnly
namestring
publicKey*string
userId*string
credentialID*string
counter*number
deviceType*string
backedUp*boolean
transportsstring
createdAtstring<date-time>
aaguidstring
example copy
[
  {
    "id": "string",
    "name": "string",
    "publicKey": "string",
    "userId": "string",
    "credentialID": "string",
    "counter": 0,
    "deviceType": "string",
    "backedUp": false,
    "transports": "string",
    "createdAt": "2026-01-01T00:00:00Z",
    "aaguid": "string"
  }
]
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

Delete a specific passkey

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/auth/passkey/delete-passkey" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{"id":"string"}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/passkey/delete-passkey", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({
    "id": "string"
  })
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/auth/passkey/delete-passkey", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={
  "id": "string"
})
data = res.json()
body
Request body (application/json)
fieldtypenotes
id*stringThe ID of the passkey to delete. Eg: "some-passkey-id" max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
Responses (by name)
200 200 (application/json) Passkey deleted successfully
fieldtypenotes
status*booleanIndicates whether the deletion was successful
example copy
{
  "status": false
}
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

Update a specific passkey's name

🔒 requires bearerAuth
copy pathcopy link
copy
curl -X POST "https://app.toolfactory.saastemly.com/api/auth/passkey/update-passkey" \
  -H "content-type: application/json" \
  -H "authorization: Bearer <token>" \
  -d '{"id":"string","name":"string"}'
const res = await fetch("https://app.toolfactory.saastemly.com/api/auth/passkey/update-passkey", {
  method: "POST",
  headers: {
    "content-type": "application/json",
    "authorization": "Bearer <token>"
  },
  body: JSON.stringify({
    "id": "string",
    "name": "string"
  })
});
const data = await res.json();
import requests
res = requests.post("https://app.toolfactory.saastemly.com/api/auth/passkey/update-passkey", headers={"content-type": "application/json", "authorization": "Bearer <token>"}, json={
  "id": "string",
  "name": "string"
})
data = res.json()
body
Request body (application/json)
fieldtypenotes
id*stringThe ID of the passkey which will be updated. Eg: "passkey-id" max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
name*stringThe new name which the passkey will be updated to. Eg: "my-new-passkey-name" min len 1max len 1024pattern ^[^\u0000-\u0008\u000b\u000c\u000e-\u001f]*$
Responses (by name)
200 200 (application/json) Passkey updated successfully
fieldtypenotes
passkey*Passkey
example copy
{
  "passkey": {
    "id": "string",
    "name": "string",
    "publicKey": "string",
    "userId": "string",
    "credentialID": "string",
    "counter": 0,
    "deviceType": "string",
    "backedUp": false,
    "transports": "string",
    "createdAt": "2026-01-01T00:00:00Z",
    "aaguid": "string"
  }
}
400 400 (application/json) Bad Request. Usually due to missing parameters, or invalid parameters.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
401 401 (application/json) Unauthorized. Due to missing or invalid authentication.
fieldtypenotes
message*string
example copy
{
  "message": "string"
}
403 403 (application/json) Forbidden. You do not have permission to access this resource or to perform this action.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
404 404 (application/json) Not Found. The requested resource was not found.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
429 429 (application/json) Too Many Requests. You have exceeded the rate limit. Try again later.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
500 500 (application/json) Internal Server Error. This is a problem with the server that you cannot fix.
fieldtypenotes
messagestring
example copy
{
  "message": "string"
}
⏱ free · rate-limited

One contract → every layer

The same v4 contract is not a document but a function — it projects into each layer below. Change the contract, and all of them change.

Hardening — input-validation grade 🛡 A 96/100

Untrusted input is the attack surface. Every string wants a maxLength + a pattern (a character allowlist), every number a maximum, every array a maxItems, every object closed (additionalProperties:false) + typed, and no any/unknown — so malformed or oversized input can't break the system. 1 high · 14 medium · 0 low findings.

🛡 F updateSession 2 findings
  • medium object 'updateSession/body' allows additionalProperties → set additionalProperties:false to forbid unexpected keys
  • medium object 'updateSession/body' has no defined properties (a free-form bag) → define properties with explicit types (a closed empty object additionalProperties:false is fine for no-input)
🛡 F postOperation 2 findings
  • medium object 'postOperation/body' allows additionalProperties → set additionalProperties:false to forbid unexpected keys
  • medium object 'postOperation/body' has no defined properties (a free-form bag) → define properties with explicit types (a closed empty object additionalProperties:false is fine for no-input)
🛡 B postOperation 3 findings
  • high 'postOperation/body/metadata' has no `type` (any/unknown) → give it a concrete type + bounds — never accept unconstrained input
  • medium object 'postOperation/body/permissions' allows additionalProperties → set additionalProperties:false to forbid unexpected keys
  • medium object 'postOperation/body/permissions' has no defined properties (a free-form bag) → define properties with explicit types (a closed empty object additionalProperties:false is fine for no-input)
🛡 B postOperation 3 findings
  • high 'postOperation/body/metadata' has no `type` (any/unknown) → give it a concrete type + bounds — never accept unconstrained input
  • medium object 'postOperation/body/permissions' allows additionalProperties → set additionalProperties:false to forbid unexpected keys
  • medium object 'postOperation/body/permissions' has no defined properties (a free-form bag) → define properties with explicit types (a closed empty object additionalProperties:false is fine for no-input)
🛡 A postOperation 4 findings
  • medium object 'postOperation/body/jwks' allows additionalProperties → set additionalProperties:false to forbid unexpected keys
  • medium object 'postOperation/body/jwks' has no defined properties (a free-form bag) → define properties with explicit types (a closed empty object additionalProperties:false is fine for no-input)
  • medium object 'postOperation/body/metadata' allows additionalProperties → set additionalProperties:false to forbid unexpected keys
  • medium object 'postOperation/body/metadata' has no defined properties (a free-form bag) → define properties with explicit types (a closed empty object additionalProperties:false is fine for no-input)
🛡 A linkSocialAccount 2 findings
  • medium object 'linkSocialAccount/body/additionalData' allows additionalProperties → set additionalProperties:false to forbid unexpected keys
  • medium object 'linkSocialAccount/body/additionalData' has no defined properties (a free-form bag) → define properties with explicit types (a closed empty object additionalProperties:false is fine for no-input)
🛡 A socialSignIn 2 findings
  • medium object 'socialSignIn/body/additionalData' allows additionalProperties → set additionalProperties:false to forbid unexpected keys
  • medium object 'socialSignIn/body/additionalData' has no defined properties (a free-form bag) → define properties with explicit types (a closed empty object additionalProperties:false is fine for no-input)

Facet panels — v4 operational facets

Advisory facets a v4 contract carries beyond shape — what a 3.x tool can't host. Rate limits: the declared per-op budget. Background costs (C024/C025): costs that accrue when an EVENT fires (a webhook / cron / queue), not when the route runs — with who gets charged.

Rate limits 68/72 operations metered

operationroutebudgetkey
getOperationGET api/auth/.well-known/oauth-authorization-server15 / 60sip
getOperationGET api/auth/.well-known/oauth-protected-resource15 / 60sip
getOperationGET api/auth/account-info15 / 60sip
postOperationPOST api/auth/api-key/create15 / 60sip
postOperationPOST api/auth/api-key/delete15 / 60sip
getOperationGET api/auth/api-key/get15 / 60sip
getOperationGET api/auth/api-key/list15 / 60sip
postOperationPOST api/auth/api-key/update15 / 60sip
getOperationGET api/auth/callback/{id}15 / 60sip
postOperationPOST api/auth/callback/{id}15 / 60sip
getOperationGET api/auth/delete-user/callback15 / 60sip
deleteUserPOST api/auth/delete-user15 / 60sip
getOperationGET api/auth/error15 / 60sip
postOperationPOST api/auth/get-access-token15 / 60sip
linkSocialAccountPOST api/auth/link-social15 / 60sip
listUserAccountsGET api/auth/list-accounts15 / 60sip
listUserSessionsGET api/auth/list-sessions15 / 60sip
getOperationGET api/auth/mcp/authorize15 / 60sip
getOperationGET api/auth/mcp/get-session15 / 60sip
postOperationPOST api/auth/mcp/register15 / 60sip
postOperationPOST api/auth/mcp/token15 / 60sip
postOperationPOST api/auth/oauth2/consent15 / 60sip
getOperationGET api/auth/ok15 / 60sip
postOperationPOST api/auth/passkey/delete-passkey15 / 60sip
passkeyGenerateAuthenticateOptionsGET api/auth/passkey/generate-authenticate-options15 / 60sip
generatePasskeyRegistrationOptionsGET api/auth/passkey/generate-register-options15 / 60sip
getOperationGET api/auth/passkey/list-user-passkeys15 / 60sip
postOperationPOST api/auth/passkey/update-passkey15 / 60sip
passkeyVerifyAuthenticationPOST api/auth/passkey/verify-authentication15 / 60sip
passkeyVerifyRegistrationPOST api/auth/passkey/verify-registration15 / 60sip
postOperationPOST api/auth/refresh-token15 / 60sip
postOperationPOST api/auth/revoke-other-sessions15 / 60sip
postOperationPOST api/auth/revoke-session15 / 60sip
postOperationPOST api/auth/revoke-sessions15 / 60sip
socialSignInPOST api/auth/sign-in/social15 / 60sip
signOutPOST api/auth/sign-out15 / 60sip
postOperationPOST api/auth/unlink-account15 / 60sip
updateSessionPOST api/auth/update-session15 / 60sip
updateUserPOST api/auth/update-user15 / 60sip
getAutoTopupGET api/billing/auto-topup30 / 60sprincipal
updateAutoTopupPOST api/billing/auto-topup30 / 60sprincipal
checkoutPOST api/billing/checkout20 / 60sprincipal
setDefaultPaymentMethodPOST api/billing/methods/default30 / 60sprincipal
deletePaymentMethodPOST api/billing/methods/delete30 / 60sprincipal
listPaymentMethodsGET api/billing/methods60 / 60sprincipal
getPacksGET api/billing/packs60 / 60sip
getPaymentConfigGET api/billing/payment-config60 / 60sip
getPaymentHealthGET api/billing/payment-health60 / 60sprincipal
createPaymentIntentPOST api/billing/payment-intent20 / 60sprincipal
getPlansGET api/billing/plans60 / 60sip
portalPOST api/billing/portal20 / 60sprincipal
getPurchaseQuoteGET api/billing/purchase-quote30 / 60sprincipal
getRefundQuoteGET api/billing/refund-quote30 / 60sprincipal
refundCreditsPOST api/billing/refund10 / 60sprincipal
createSetupIntentPOST api/billing/setup-intent20 / 60sprincipal
subscribePOST api/billing/subscribe20 / 60sprincipal
changePlanPOST api/billing/subscription-plan20 / 60sprincipal
cancelSubscriptionPOST api/billing/subscription20 / 60sprincipal
getSubscriptionGET api/billing/subscription60 / 60sprincipal
listTransactionsGET api/credits/transactions60 / 60sprincipal
getCreditsGET api/credits60 / 60sprincipal
provisionKeyPOST api/keys/provision30 / 60sprincipal
revokeKeyPOST api/keys/revoke30 / 60sprincipal
getApiKeysGET api/keys60 / 60sprincipal
getLogsGET api/logs60 / 60sprincipal
revokeMcpConnectionPOST api/mcp/connections/revoke30 / 60sprincipal
updateMcpConnectionPOST api/mcp/connections/update30 / 60sprincipal
getMcpConnectionsGET api/mcp/connections60 / 60sprincipal

Background-event costs 0 deferred

no background-triggered costs — every cost accrues on its own route.

Cost Explorer + Workflow Calculator

Every operation by declared cost + source (a contract fact, not telemetry). Tick a sequence of operations to build a WORKFLOW — see its cumulative cost + per-source breakdown + a monthly projection. Budget a user journey before you build it. No 3.x tool models cost at all.

Workflow: $0 ·
operationcost ⇅computed1stripeworkers-ai-whisper
transcribe$0.00225 2250µ$1µ$2µ$450µ$
refundCredits6 µ$ 6µ$1µ$2µ$3µ$
getPurchaseQuote5 µ$ 5µ$1µ$1µ$3µ$
changePlan5 µ$ 5µ$1µ$1µ$3µ$
getRefundQuote5 µ$ 5µ$1µ$2µ$2µ$
provisionKey4 µ$ 4µ$1µ$3µ$
listPaymentMethods4 µ$ 4µ$1µ$1µ$2µ$
setDefaultPaymentMethod4 µ$ 4µ$1µ$1µ$2µ$
deletePaymentMethod4 µ$ 4µ$1µ$1µ$2µ$
getLogs3 µ$ 3µ$1µ$2µ$
getApiKeys3 µ$ 3µ$1µ$2µ$
revokeKey3 µ$ 3µ$1µ$2µ$
getMcpConnections3 µ$ 3µ$1µ$2µ$
updateMcpConnection3 µ$ 3µ$1µ$2µ$
getSubscription3 µ$ 3µ$1µ$1µ$1µ$
cancelSubscription3 µ$ 3µ$1µ$1µ$1µ$
updateAutoTopup3 µ$ 3µ$1µ$1µ$1µ$
convertSubtitle3 µ$ 3µ$3µ$2µ$
getSession2 µ$ 2µ$1µ$1µ$
getCredits2 µ$ 2µ$1µ$1µ$
listTransactions2 µ$ 2µ$1µ$1µ$
createPaymentIntent2 µ$ 2µ$1µ$1µ$
getPaymentHealth2 µ$ 2µ$1µ$1µ$
revokeMcpConnection2 µ$ 2µ$1µ$1µ$
createSetupIntent2 µ$ 2µ$1µ$1µ$
subscribe2 µ$ 2µ$1µ$1µ$
portal2 µ$ 2µ$1µ$1µ$
getAutoTopup2 µ$ 2µ$1µ$1µ$
health1 µ$ 1µ$1µ$
getPacks1 µ$ 1µ$1µ$
getPlans1 µ$ 1µ$1µ$
checkout1 µ$ 1µ$1µ$
getPaymentConfig1 µ$ 1µ$1µ$
socialSignIn1 µ$ 1µ$1µ$
getOperation1 µ$ 1µ$1µ$
postOperation1 µ$ 1µ$1µ$
signOut1 µ$ 1µ$1µ$
updateSession1 µ$ 1µ$1µ$
updateUser1 µ$ 1µ$1µ$
deleteUser1 µ$ 1µ$1µ$
listUserSessions1 µ$ 1µ$1µ$
postOperation1 µ$ 1µ$1µ$
postOperation1 µ$ 1µ$1µ$
postOperation1 µ$ 1µ$1µ$
linkSocialAccount1 µ$ 1µ$1µ$
listUserAccounts1 µ$ 1µ$1µ$
getOperation1 µ$ 1µ$1µ$
postOperation1 µ$ 1µ$1µ$
postOperation1 µ$ 1µ$1µ$
postOperation1 µ$ 1µ$1µ$
getOperation1 µ$ 1µ$1µ$
getOperation1 µ$ 1µ$1µ$
getOperation1 µ$ 1µ$1µ$
postOperation1 µ$ 1µ$1µ$
getOperation1 µ$ 1µ$1µ$
postOperation1 µ$ 1µ$1µ$
postOperation1 µ$ 1µ$1µ$
getOperation1 µ$ 1µ$1µ$
generatePasskeyRegistrationOptions1 µ$ 1µ$1µ$
passkeyGenerateAuthenticateOptions1 µ$ 1µ$1µ$
passkeyVerifyRegistration1 µ$ 1µ$1µ$
passkeyVerifyAuthentication1 µ$ 1µ$1µ$
getOperation1 µ$ 1µ$1µ$
postOperation1 µ$ 1µ$1µ$
postOperation1 µ$ 1µ$1µ$
postOperation1 µ$ 1µ$1µ$
getOperation1 µ$ 1µ$1µ$
getOperation1 µ$ 1µ$1µ$
getOperation1 µ$ 1µ$1µ$
postOperation1 µ$ 1µ$1µ$
postOperation1 µ$ 1µ$1µ$
getOperation1 µ$ 1µ$1µ$

ADA Resolution Playground

v4 dispatches a request to a NAMED operation by its computed signature, not by method+path (which need not be unique). Enter a request and watch it resolve — uniquely, to a collision, or to a runtime-dependent set. A question 3.x never had to ask.

Enter a request and press Resolve.

Reachability — the contract refracted per viewer

Access is a contract facet (x-suluk-access). ● full · ◐ own rows only · · not reachable. The View as lens recomputes the visible operation set from this.

operationrequiresAnonymousSigned-in userAdmin
healthanyone
getSessionanyone
getCreditsauthenticated·
listTransactionsauthenticated·
getLogsauthenticated·
getPacksanyone
getPlansanyone
checkoutauthenticated·
getPaymentConfiganyone
createPaymentIntentauthenticated·
getPaymentHealthauthenticated·
getApiKeysauthenticated·
provisionKeyauthenticated·
revokeKeyauthenticated·
getMcpConnectionsauthenticated·
updateMcpConnectionauthenticated·
revokeMcpConnectionauthenticated·
getPurchaseQuoteauthenticated·
createSetupIntentauthenticated·
subscribeauthenticated·
getSubscriptionauthenticated·
cancelSubscriptionauthenticated·
changePlanauthenticated·
getRefundQuoteauthenticated·
refundCreditsauthenticated·
portalauthenticated·
listPaymentMethodsauthenticated·
getAutoTopupauthenticated·
updateAutoTopupauthenticated·
setDefaultPaymentMethodauthenticated·
deletePaymentMethodauthenticated·
transcribeauthenticated·
convertSubtitleauthenticated·
socialSignInanyone
getOperationanyone
postOperationanyone
signOutauthenticated·
updateSessionauthenticated·
updateUserauthenticated·
deleteUserauthenticated·
listUserSessionsauthenticated·
postOperationauthenticated·
postOperationauthenticated·
postOperationauthenticated·
linkSocialAccountauthenticated·
listUserAccountsauthenticated·
getOperationauthenticated·
postOperationauthenticated·
postOperationauthenticated·
postOperationauthenticated·
getOperationauthenticated·
getOperationanyone
getOperationanyone
postOperationauthenticated·
getOperationauthenticated·
postOperationauthenticated·
postOperationauthenticated·
getOperationauthenticated·
generatePasskeyRegistrationOptionsauthenticated·
passkeyGenerateAuthenticateOptionsanyone
passkeyVerifyRegistrationauthenticated·
passkeyVerifyAuthenticationanyone
getOperationauthenticated·
postOperationauthenticated·
postOperationauthenticated·
postOperationauthenticated·
getOperationanyone
getOperationanyone
getOperationanyone
postOperationanyone
postOperationanyone
getOperationauthenticated·

Models

ProblemDetails

fieldtypenotes
type*string<uri-reference> = "about:blank"
title*string
status*integer
detailstring
instancestring<uri-reference>
errorsobject
errorstring deprecated

User

fieldtypenotes
id*string readOnly
name*string
email*string
emailVerified*boolean = false readOnly
imagestring
createdAt*string<date-time>
updatedAt*string<date-time>

Session

fieldtypenotes
id*string readOnly
expiresAt*string<date-time>
token*string
createdAt*string<date-time>
updatedAt*string<date-time>
ipAddressstring
userAgentstring
userId*string

Account

fieldtypenotes
id*string readOnly
accountId*string
providerId*string
userId*string
accessTokenstring
refreshTokenstring
idTokenstring
accessTokenExpiresAtstring<date-time>
refreshTokenExpiresAtstring<date-time>
scopestring
passwordstring
createdAt*string<date-time>
updatedAt*string<date-time>

Verification

fieldtypenotes
id*string readOnly
identifier*string
value*string
expiresAt*string<date-time>
createdAt*string<date-time>
updatedAt*string<date-time>

Apikey

fieldtypenotes
id*string readOnly
configId*string = "default" readOnly
namestring readOnly
startstring readOnly
referenceId*string readOnly
prefixstring readOnly
key*string readOnly
refillIntervalnumber readOnly
refillAmountnumber readOnly
lastRefillAtstring<date-time> readOnly
enabledboolean = true readOnly
rateLimitEnabledboolean = true readOnly
rateLimitTimeWindownumber = 86400000 readOnly
rateLimitMaxnumber = 10 readOnly
requestCountnumber = 0 readOnly
remainingnumber readOnly
lastRequeststring<date-time> readOnly
expiresAtstring<date-time> readOnly
createdAt*string<date-time> readOnly
updatedAt*string<date-time> readOnly
permissionsstring readOnly
metadatastring

Passkey

fieldtypenotes
id*string readOnly
namestring
publicKey*string
userId*string
credentialID*string
counter*number
deviceType*string
backedUp*boolean
transportsstring
createdAtstring<date-time>
aaguidstring

OauthApplication

fieldtypenotes
id*string readOnly
namestring
iconstring
metadatastring
clientIdstring
clientSecretstring
redirectUrlsstring
typestring
disabledboolean = false
userIdstring
createdAtstring<date-time>
updatedAtstring<date-time>

OauthAccessToken

fieldtypenotes
id*string readOnly
accessTokenstring
refreshTokenstring
accessTokenExpiresAtstring<date-time>
refreshTokenExpiresAtstring<date-time>
clientIdstring
userIdstring
scopesstring
createdAtstring<date-time>
updatedAtstring<date-time>

OauthConsent

fieldtypenotes
id*string readOnly
clientIdstring
userIdstring
scopesstring
createdAtstring<date-time>
updatedAtstring<date-time>
consentGivenboolean

Authentication

sessionCookie apiKey · in cookie
apiKeyCookie apiKey · in cookie

API Key authentication via cookie

bearerAuth http · bearer

Bearer token authentication