Skip to content

Errors

Every error response from the Aware Public API uses the same JSON envelope:

{
"type": "CheckinNotFoundError",
"message": "Checkin Not Found! No checkin found with id: 8f5b9a4d-3c2e-4b1f-9d8a-72e6c1f0a4b3"
}

type is a stable, machine-readable string (the error class name, except where noted below). message is human-readable and may change wording across releases. Some errors include an errorId for support correlation — see below.

BadRequestError may also include an errors array of { field, message } when validation failed on specific fields.

HTTPtypeWhen you’ll see it
400BadRequestErrorA request field failed validation (missing, wrong type, out of range).
401UnauthorizedErrorMissing or invalid Authorization: Bearer JWT.
403ForbiddenErrorAuthenticated caller is not allowed to perform this operation.
404NotFoundErrorGeneric “we don’t have that, or you can’t see it”.
404CheckinNotFoundErrorA specific check-in lookup found nothing in the caller’s scope.
404NoBreaksOnCheckinErrorA break lookup found no breaks on that check-in.
404PersonNotFoundErrorA specific person lookup found nothing in the caller’s scope.
404LabelNotFoundErrorA specific label lookup found nothing in the caller’s scope.
409IdempotencyConflictThe same Idempotency-Key is already in flight on this endpoint.
409LastBreakAlreadyEndedErrorTried to end a break that’s already ended.
409NoActiveBreakToEndErrorTried to end a break but the check-in has no active break.
409ExitBeforeEntryErrorCheckout timestamp is before the check-in entry time.
429RateLimitedRate limit exceeded. See Rate limits.
503ServiceUnavailableA dependency required to complete the request is temporarily unavailable.
500InternalUnexpected server failure. Includes errorId; send it to support.

This list is closed for /v1. We don’t add new type values without a Changelog entry, and we never remove or rename one in /v1.

A simple, robust client-side mapping:

if (res.ok) return res.json()
const err = (await res.json()) as {
type: string
message: string
errorId?: string
errors?: { field: string; message: string }[]
}
switch (err.type) {
case 'BadRequestError':
throw new BadRequestError(err.message, err.errors)
case 'UnauthorizedError':
case 'ForbiddenError':
throw new AuthError(err.message)
case 'CheckinNotFoundError':
case 'NoBreaksOnCheckinError':
case 'PersonNotFoundError':
case 'LabelNotFoundError':
case 'NotFoundError':
throw new NotFoundError(err.message)
case 'IdempotencyConflict':
case 'LastBreakAlreadyEndedError':
case 'NoActiveBreakToEndError':
case 'ExitBeforeEntryError':
throw new ConflictError(err.message)
case 'RateLimited':
throw new RetryLaterError(err.message, res.headers.get('Retry-After'))
case 'ServiceUnavailable':
throw new RetryLaterError(err.message, res.headers.get('Retry-After'))
case 'Internal':
throw new InternalError(err.message, err.errorId)
default:
throw new UnknownAwareError(err.type, err.message)
}

You can safely treat any type you don’t recognise as “unknown error from Aware” — we won’t introduce a new one in /v1 without a Changelog entry, but tolerant clients are still a good habit.

Anything we can’t classify becomes:

{
"type": "Internal",
"message": "An unexpected error occurred.",
"errorId": "3f2a..."
}

The errorId is unique to that request. Send it to Aware support and we can find the corresponding log entry.

  • BadRequestError is for shape, not domain rules. A missing required field is BadRequestError. Trying to end a break that’s already ended is LastBreakAlreadyEndedError (409), not BadRequestError.
  • 404 vs 403. When your caller cannot see a resource that exists, we deliberately return 404 rather than 403. This stops probing for resources outside authorized scope.
  • Always read Retry-After. Don’t infer back-off from the message — parse the header.