Skip to main content
All Integration API errors follow RFC 7807 Problem Details (application/problem+json). A single schema covers every failure mode — auth, validation, concurrency, rate-limiting, server errors.

Problem shape

{
  "type": "https://api.novatrade24.com/problems/validation-error",
  "title": "Validation Error",
  "status": 422,
  "detail": "Field 'vatNumber' must match pattern ^[A-Z]{2}[0-9A-Z]+$",
  "instance": "/v1/partners/xxx/buyers",
  "traceId": "req_a1b2c3d4",
  "errors": [
    {
      "field": "vatNumber",
      "message": "must match pattern ^[A-Z]{2}[0-9A-Z]+$",
      "rejectedValue": "invalid"
    }
  ]
}
FieldTypeNotes
typeURI stringStable identifier for the error category. Keyed on this, not on status or title.
titlestringHuman-readable summary. Safe to display.
statusintegerHTTP status code, duplicated for convenience.
detailstringHuman-readable specifics of this particular occurrence.
instancestringRequest path.
traceIdstringCorrelation id. Include in support requests.
errors[]arrayValidation-error only — per-field breakdown.
Always key on type, not on status or title. We may add new problem types over time; we will not change existing ones without a version bump.

Status-code catalog

StatusMeaningTypical type URIs
400Malformed request…/problems/malformed-request
401Missing or invalid token…/problems/unauthorized
403Authenticated but not authorized (partner scope, capability, DPA)…/problems/forbidden, …/problems/capability-required, …/problems/dpa-not-accepted
404Resource not found…/problems/not-found
409State conflict (If-Match mismatch, natural-key collision, milestone already set)…/problems/version-mismatch, …/problems/resource-exists-different-body, …/problems/milestone-already-set
422Validation failed or idempotency key reused with different body…/problems/validation-error, …/problems/idempotency-key-reused
429Rate limit exceeded…/problems/rate-limited
500Unexpected server error…/problems/internal-error

Handling examples

async function call(url: string, init: RequestInit) {
  const res = await fetch(url, init);
  if (res.ok) return res.json();

  const problem = await res.json();
  switch (problem.type) {
    case 'https://api.novatrade24.com/problems/version-mismatch':
      // Refetch and retry with new ETag.
      throw new VersionMismatch(problem);

    case 'https://api.novatrade24.com/problems/rate-limited':
      const retryAfter = parseInt(res.headers.get('Retry-After') ?? '1', 10);
      await new Promise((r) => setTimeout(r, retryAfter * 1000));
      return call(url, init);

    case 'https://api.novatrade24.com/problems/dpa-not-accepted':
      alertOps('Novatrade24 DPA needs re-acceptance', problem);
      throw new DpaStale(problem);

    default:
      throw new ApiError(res.status, problem);
  }
}

Validation errors (422)

Body includes an errors[] array with per-field detail:
{
  "type": "https://api.novatrade24.com/problems/validation-error",
  "title": "Validation Error",
  "status": 422,
  "detail": "Request body failed validation. See errors[].",
  "instance": "/v1/partners/.../buyers",
  "traceId": "req_a1b2c3d4",
  "errors": [
    {
      "field": "vatNumber",
      "message": "must match pattern ^[A-Z]{2}[0-9A-Z]+$",
      "rejectedValue": "invalid"
    },
    {
      "field": "address.country",
      "message": "must be a 2-letter ISO-3166-1 alpha-2 code",
      "rejectedValue": "ger"
    }
  ]
}
Show the per-field messages in your UI; root-level detail is a summary.

Concurrency conflicts (409 with version mismatch)

When a PATCH carries an If-Match header that doesn’t match the current entity version, NT24 returns 409 with the full current resource body — you don’t need a separate GET to reconcile.
{
  "type": "https://api.novatrade24.com/problems/version-mismatch",
  "title": "Version Mismatch",
  "status": 409,
  "detail": "If-Match version 3 does not match current version 5.",
  "instance": "/v1/partners/.../orders/...",
  "traceId": "req_...",
  "currentResource": {
    "uuid": "...",
    "version": 5,
    "...": "..."
  }
}
Merge locally and retry with the new ETag.

Idempotency key reuse (422)

If you reuse an Idempotency-Key within the 24-hour window with a different body, NT24 rejects with 422:
{
  "type": "https://api.novatrade24.com/problems/idempotency-key-reused",
  "title": "Idempotency Key Reused With Different Body",
  "status": 422,
  "detail": "Key 'abc-123' was first used for a different request. Use a new key.",
  "instance": "/v1/partners/.../buyers",
  "traceId": "req_..."
}
Generate fresh UUIDs for each logically-distinct request.

Rate limiting (429)

{
  "type": "https://api.novatrade24.com/problems/rate-limited",
  "title": "Rate Limit Exceeded",
  "status": 429,
  "detail": "60 requests per minute for write category exceeded.",
  "instance": "/v1/partners/.../orders",
  "traceId": "req_..."
}
Response headers carry the budget state — honor Retry-After:
Retry-After: 37
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1729684260

Troubleshooting

Your token is valid, but the specific partner in the URL path isn’t in your authorized partners list — OR a capability required for the endpoint (e.g. allowMarketplaceLedKyc) is false on your org. Check GET /v1/me.
The resource exists already with a different body than what you’re sending. Either re-send the exact original body, or switch to PATCH to update fields explicitly. Safe retries require both the same Idempotency-Key AND the same body.
Non-validation 422 — most commonly idempotency-key reuse with different body. Check type.
Capture traceId from the response body and include it when reaching our contact page. We correlate logs by trace id.

Next

Rate limits

Tier breakdowns and burst behavior.

Idempotency

Key format, TTL, replay semantics.