Upload a new version
const url = 'https://api.tradecrafthub.ai/v1/documents/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/versions';const form = new FormData();form.append('file', 'file');
const options = { method: 'POST', headers: {'Idempotency-Key': 'example', Authorization: 'Bearer <token>'}};
options.body = form;
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://api.tradecrafthub.ai/v1/documents/2489E9AD-2EE2-8E00-8EC9-32D5F69181C0/versions \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: multipart/form-data' \ --header 'Idempotency-Key: example' \ --form file=@fileReviewer and up. Up to 25 MB (a larger file is 400 VALIDATION_ERROR). Same content as an existing version → 200 deduplicated.
Scope library:write (Add documents; lowest role reviewer). Effective access is the token’s scopes ∩ the caller’s current role.
Idempotency-Key required: a retry with the same key and request replays the stored response (Idempotent-Replayed: true); another request with the key is 409 IDEMPOTENCY_CONFLICT.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Path Parameters
Section titled “Path Parameters”Document id
Header Parameters
Section titled “Header Parameters”Your correlation id (8–128 of A–Z a–z 0–9 . _ : -); echoed back
Required: 1–255 visible ASCII characters; kept 24 h per (organization, principal, operation)
Request Bodyrequired
Section titled “Request Bodyrequired”object
Responses
Section titled “Responses”Stored; ingest started
object
object
Example generated
{ "data": { "versionId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "version": 1, "deduped": true }}Headers
Section titled “Headers”Echoes a valid X-Request-Id, else generated
Requests allowed in the tightest window
Requests left
Seconds until the window resets
VALIDATION_ERROR— The request body, query or path is invalid.ASSET_KIND_NOT_ALLOWED— The library type does not accept this asset kind.
Also a token in the URL (the token is revoked), a missing Idempotency-Key where required, a cursor from another organization or filter set.
object
object
The error-code catalogue: VALIDATION_ERROR 400, UNAUTHENTICATED 401, SPEND_CAP_EXCEEDED 402, TENANT_AI_BUDGET_EXCEEDED 402, FORBIDDEN 403, NOT_FOUND 404, CONFLICT 409, GONE 410, ENDPOINT_REMOVED 410, PAYLOAD_TOO_LARGE 413, UNSUPPORTED_MEDIA_TYPE 415, RATE_LIMITED 429, INTERNAL 500, PROVIDER_ERROR 502, SERVICE_UNAVAILABLE 503, WEAK_PASSWORD 400, INVALID_TOKEN 400, EXPIRED_TOKEN 400, INVALID_CREDENTIALS 401, INVALID_CODE 401, SSO_REQUIRED 403, SLUG_TAKEN 409, LAST_ADMIN 409, MANAGED_BY_IDP 409, LOCKED 429, ASSET_KIND_NOT_ALLOWED 400, ASSET_SOURCE_NOT_ALLOWED 400, LIBRARY_ARCHIVED 409, MISSING_SCOPES 400, CLASSIC_TOKEN 400, INVALID_CREDENTIAL 400, CLOUD_ID_UNRESOLVED 400, SITE_NOT_FOUND 400, INTEGRATION_AUTH 409, INTEGRATION_NOT_CONNECTED 409, SITE_CHOICE_EXPIRED 409, PLATFORM_NOT_CONFIGURED 503, PLAN_LIMIT_REACHED 403, PLAN_FEATURE_UNAVAILABLE 403, TRIAL_EXPIRED 403, INSUFFICIENT_SCOPE 403, API_DISABLED 403, IDEMPOTENCY_CONFLICT 409. Extensible.
Human-readable message
INSUFFICIENT_SCOPE: the scope the operation needs
FORBIDDEN: why (member_inactive, permission_missing, not_opted_in, user_only, role, …)
object
Examples
The request body, query or path is invalid.
{ "error": { "code": "VALIDATION_ERROR", "message": "limit must be between 1 and 100" }}The library type does not accept this asset kind.
{ "error": { "code": "ASSET_KIND_NOT_ALLOWED", "message": "InfoSec & GRC libraries do not accept \"link\" assets" }}UNAUTHENTICATED— No valid session, API key or bearer token.
Missing, unknown, expired or revoked token, or a token for another resource (WWW-Authenticate: Bearer … names the resource metadata).
object
object
The error-code catalogue: VALIDATION_ERROR 400, UNAUTHENTICATED 401, SPEND_CAP_EXCEEDED 402, TENANT_AI_BUDGET_EXCEEDED 402, FORBIDDEN 403, NOT_FOUND 404, CONFLICT 409, GONE 410, ENDPOINT_REMOVED 410, PAYLOAD_TOO_LARGE 413, UNSUPPORTED_MEDIA_TYPE 415, RATE_LIMITED 429, INTERNAL 500, PROVIDER_ERROR 502, SERVICE_UNAVAILABLE 503, WEAK_PASSWORD 400, INVALID_TOKEN 400, EXPIRED_TOKEN 400, INVALID_CREDENTIALS 401, INVALID_CODE 401, SSO_REQUIRED 403, SLUG_TAKEN 409, LAST_ADMIN 409, MANAGED_BY_IDP 409, LOCKED 429, ASSET_KIND_NOT_ALLOWED 400, ASSET_SOURCE_NOT_ALLOWED 400, LIBRARY_ARCHIVED 409, MISSING_SCOPES 400, CLASSIC_TOKEN 400, INVALID_CREDENTIAL 400, CLOUD_ID_UNRESOLVED 400, SITE_NOT_FOUND 400, INTEGRATION_AUTH 409, INTEGRATION_NOT_CONNECTED 409, SITE_CHOICE_EXPIRED 409, PLATFORM_NOT_CONFIGURED 503, PLAN_LIMIT_REACHED 403, PLAN_FEATURE_UNAVAILABLE 403, TRIAL_EXPIRED 403, INSUFFICIENT_SCOPE 403, API_DISABLED 403, IDEMPOTENCY_CONFLICT 409. Extensible.
Human-readable message
INSUFFICIENT_SCOPE: the scope the operation needs
FORBIDDEN: why (member_inactive, permission_missing, not_opted_in, user_only, role, …)
object
Examples
No valid session, API key or bearer token.
{ "error": { "code": "UNAUTHENTICATED", "message": "The access token is invalid, expired or revoked" }}FORBIDDEN— Authenticated, but the role, object grant or policy does not allow this.INSUFFICIENT_SCOPE— The access token lacks the scope this operation needs (error.scope names it; WWW-Authenticate: Bearer error=“insufficient_scope”).API_DISABLED— The organization has turned the API off (Admin → API → Settings); its clients and grants are kept.PLAN_FEATURE_UNAVAILABLE— The organization’s plan does not include this feature (error.feature, plan).TRIAL_EXPIRED— The trial ended (or the subscription was cancelled); the organization is read-only (error.trialEndedAt).
object
object
The error-code catalogue: VALIDATION_ERROR 400, UNAUTHENTICATED 401, SPEND_CAP_EXCEEDED 402, TENANT_AI_BUDGET_EXCEEDED 402, FORBIDDEN 403, NOT_FOUND 404, CONFLICT 409, GONE 410, ENDPOINT_REMOVED 410, PAYLOAD_TOO_LARGE 413, UNSUPPORTED_MEDIA_TYPE 415, RATE_LIMITED 429, INTERNAL 500, PROVIDER_ERROR 502, SERVICE_UNAVAILABLE 503, WEAK_PASSWORD 400, INVALID_TOKEN 400, EXPIRED_TOKEN 400, INVALID_CREDENTIALS 401, INVALID_CODE 401, SSO_REQUIRED 403, SLUG_TAKEN 409, LAST_ADMIN 409, MANAGED_BY_IDP 409, LOCKED 429, ASSET_KIND_NOT_ALLOWED 400, ASSET_SOURCE_NOT_ALLOWED 400, LIBRARY_ARCHIVED 409, MISSING_SCOPES 400, CLASSIC_TOKEN 400, INVALID_CREDENTIAL 400, CLOUD_ID_UNRESOLVED 400, SITE_NOT_FOUND 400, INTEGRATION_AUTH 409, INTEGRATION_NOT_CONNECTED 409, SITE_CHOICE_EXPIRED 409, PLATFORM_NOT_CONFIGURED 503, PLAN_LIMIT_REACHED 403, PLAN_FEATURE_UNAVAILABLE 403, TRIAL_EXPIRED 403, INSUFFICIENT_SCOPE 403, API_DISABLED 403, IDEMPOTENCY_CONFLICT 409. Extensible.
Human-readable message
INSUFFICIENT_SCOPE: the scope the operation needs
FORBIDDEN: why (member_inactive, permission_missing, not_opted_in, user_only, role, …)
object
Examples
Authenticated, but the role, object grant or policy does not allow this.
{ "error": { "code": "FORBIDDEN", "message": "Your current role can't do this", "reason": "role" }}The access token lacks the scope this operation needs (error.scope names it; WWW-Authenticate: Bearer error="insufficient_scope").
{ "error": { "code": "INSUFFICIENT_SCOPE", "message": "This operation needs the library:write scope", "scope": "library:write" }}The organization has turned the API off (Admin → API → Settings); its clients and grants are kept.
{ "error": { "code": "API_DISABLED", "message": "The API is turned off for this organization. Ask an admin to turn it on in Admin → API." }}The organization's plan does not include this feature (error.feature, plan).
{ "error": { "code": "PLAN_FEATURE_UNAVAILABLE", "message": "The Tradecraft API is not included in your Pro plan. Available on Enterprise.", "feature": "api.rest", "plan": "pro" }}The trial ended (or the subscription was cancelled); the organization is read-only (error.trialEndedAt).
{ "error": { "code": "TRIAL_EXPIRED", "message": "Your trial ended on 2026-11-30. The organization is read-only — upgrade to keep working.", "trialEndedAt": "2026-11-30T00:00:00.000Z" }}NOT_FOUND— No such resource in this organization (unknown, malformed or other-tenant id).
Unknown, malformed or another organization’s id.
object
object
The error-code catalogue: VALIDATION_ERROR 400, UNAUTHENTICATED 401, SPEND_CAP_EXCEEDED 402, TENANT_AI_BUDGET_EXCEEDED 402, FORBIDDEN 403, NOT_FOUND 404, CONFLICT 409, GONE 410, ENDPOINT_REMOVED 410, PAYLOAD_TOO_LARGE 413, UNSUPPORTED_MEDIA_TYPE 415, RATE_LIMITED 429, INTERNAL 500, PROVIDER_ERROR 502, SERVICE_UNAVAILABLE 503, WEAK_PASSWORD 400, INVALID_TOKEN 400, EXPIRED_TOKEN 400, INVALID_CREDENTIALS 401, INVALID_CODE 401, SSO_REQUIRED 403, SLUG_TAKEN 409, LAST_ADMIN 409, MANAGED_BY_IDP 409, LOCKED 429, ASSET_KIND_NOT_ALLOWED 400, ASSET_SOURCE_NOT_ALLOWED 400, LIBRARY_ARCHIVED 409, MISSING_SCOPES 400, CLASSIC_TOKEN 400, INVALID_CREDENTIAL 400, CLOUD_ID_UNRESOLVED 400, SITE_NOT_FOUND 400, INTEGRATION_AUTH 409, INTEGRATION_NOT_CONNECTED 409, SITE_CHOICE_EXPIRED 409, PLATFORM_NOT_CONFIGURED 503, PLAN_LIMIT_REACHED 403, PLAN_FEATURE_UNAVAILABLE 403, TRIAL_EXPIRED 403, INSUFFICIENT_SCOPE 403, API_DISABLED 403, IDEMPOTENCY_CONFLICT 409. Extensible.
Human-readable message
INSUFFICIENT_SCOPE: the scope the operation needs
FORBIDDEN: why (member_inactive, permission_missing, not_opted_in, user_only, role, …)
object
Examples
No such resource in this organization (unknown, malformed or other-tenant id).
{ "error": { "code": "NOT_FOUND", "message": "Not found" }}CONFLICT— The resource’s state does not allow this now (wrong lifecycle state, duplicate, already running).IDEMPOTENCY_CONFLICT— The Idempotency-Key was already used for a different request, or the first request with it is still running.LIBRARY_ARCHIVED— The library is archived and read-only.
object
object
The error-code catalogue: VALIDATION_ERROR 400, UNAUTHENTICATED 401, SPEND_CAP_EXCEEDED 402, TENANT_AI_BUDGET_EXCEEDED 402, FORBIDDEN 403, NOT_FOUND 404, CONFLICT 409, GONE 410, ENDPOINT_REMOVED 410, PAYLOAD_TOO_LARGE 413, UNSUPPORTED_MEDIA_TYPE 415, RATE_LIMITED 429, INTERNAL 500, PROVIDER_ERROR 502, SERVICE_UNAVAILABLE 503, WEAK_PASSWORD 400, INVALID_TOKEN 400, EXPIRED_TOKEN 400, INVALID_CREDENTIALS 401, INVALID_CODE 401, SSO_REQUIRED 403, SLUG_TAKEN 409, LAST_ADMIN 409, MANAGED_BY_IDP 409, LOCKED 429, ASSET_KIND_NOT_ALLOWED 400, ASSET_SOURCE_NOT_ALLOWED 400, LIBRARY_ARCHIVED 409, MISSING_SCOPES 400, CLASSIC_TOKEN 400, INVALID_CREDENTIAL 400, CLOUD_ID_UNRESOLVED 400, SITE_NOT_FOUND 400, INTEGRATION_AUTH 409, INTEGRATION_NOT_CONNECTED 409, SITE_CHOICE_EXPIRED 409, PLATFORM_NOT_CONFIGURED 503, PLAN_LIMIT_REACHED 403, PLAN_FEATURE_UNAVAILABLE 403, TRIAL_EXPIRED 403, INSUFFICIENT_SCOPE 403, API_DISABLED 403, IDEMPOTENCY_CONFLICT 409. Extensible.
Human-readable message
INSUFFICIENT_SCOPE: the scope the operation needs
FORBIDDEN: why (member_inactive, permission_missing, not_opted_in, user_only, role, …)
object
Examples
The resource's state does not allow this now (wrong lifecycle state, duplicate, already running).
{ "error": { "code": "CONFLICT", "message": "Cannot process assessment with status \"uploaded\". Must be \"analyzed\" (run Analyze first)." }}The Idempotency-Key was already used for a different request, or the first request with it is still running.
{ "error": { "code": "IDEMPOTENCY_CONFLICT", "message": "This Idempotency-Key was used for a different request" }}The library is archived and read-only.
{ "error": { "code": "LIBRARY_ARCHIVED", "message": "This library is archived and read-only" }}RATE_LIMITED— Too many requests; retry after the Retry-After header.
Retry-After and RateLimit-* tell when to retry.
object
object
The error-code catalogue: VALIDATION_ERROR 400, UNAUTHENTICATED 401, SPEND_CAP_EXCEEDED 402, TENANT_AI_BUDGET_EXCEEDED 402, FORBIDDEN 403, NOT_FOUND 404, CONFLICT 409, GONE 410, ENDPOINT_REMOVED 410, PAYLOAD_TOO_LARGE 413, UNSUPPORTED_MEDIA_TYPE 415, RATE_LIMITED 429, INTERNAL 500, PROVIDER_ERROR 502, SERVICE_UNAVAILABLE 503, WEAK_PASSWORD 400, INVALID_TOKEN 400, EXPIRED_TOKEN 400, INVALID_CREDENTIALS 401, INVALID_CODE 401, SSO_REQUIRED 403, SLUG_TAKEN 409, LAST_ADMIN 409, MANAGED_BY_IDP 409, LOCKED 429, ASSET_KIND_NOT_ALLOWED 400, ASSET_SOURCE_NOT_ALLOWED 400, LIBRARY_ARCHIVED 409, MISSING_SCOPES 400, CLASSIC_TOKEN 400, INVALID_CREDENTIAL 400, CLOUD_ID_UNRESOLVED 400, SITE_NOT_FOUND 400, INTEGRATION_AUTH 409, INTEGRATION_NOT_CONNECTED 409, SITE_CHOICE_EXPIRED 409, PLATFORM_NOT_CONFIGURED 503, PLAN_LIMIT_REACHED 403, PLAN_FEATURE_UNAVAILABLE 403, TRIAL_EXPIRED 403, INSUFFICIENT_SCOPE 403, API_DISABLED 403, IDEMPOTENCY_CONFLICT 409. Extensible.
Human-readable message
INSUFFICIENT_SCOPE: the scope the operation needs
FORBIDDEN: why (member_inactive, permission_missing, not_opted_in, user_only, role, …)
object
Examples
Too many requests; retry after the Retry-After header.
{ "error": { "code": "RATE_LIMITED", "message": "Too many requests — retry after the Retry-After header" }}