An official website of the American Samoan government
English|Gagana Sāmoa

Official websites use .as.gov

A .as.gov website belongs to an official government organization in American Samoa.

Secure .gov.ws websites use HTTPS

A Lock or https:// means you've safely connected to a .as.gov website. Share sensitive information only on official, secure websites.

Developer Resourcesv1.0.0

API Documentation

Integrate with the TravelPass American Samoa system

Get API Token

ASDAC API Documentation

The ASDAC Public API exposes traveler declaration data, aggregate statistics, and reference lookups so partners and integrators can build on top of the American Samoa Declaration & Customs platform.

Base URL

/api/v1

All responses are JSON. Successful responses are wrapped in a top-level data field; list responses additionally include a pagination object.

Quick Start

  1. Obtain an API token from your administrator (admin dashboard → API Tokens).
  2. Send the token as a Bearer credential in the Authorization header.
  3. Make requests against any endpoint your token has permission for. Reference and health endpoints are public.
Test API Connection
curl -X GET "/api/v1/health"

Authentication

Protected endpoints require a Bearer token in the Authorization header. Tokens are issued by an administrator from the admin dashboard and are shown in full only once at creation time — store yours immediately.

Authorization Header Format

Authorization: Bearer <your-api-token>

Tokens are SHA-256 hashed at rest. Each token has a status of active, expired, or revoked; only active tokens authenticate.

Authenticated Request
curl -X GET "/api/v1/declarations" \
  -H "Authorization: Bearer your_api_token_here"

Token Permissions

Each token is granted one or more permission scopes. A request is rejected with 403 FORBIDDEN if the token does not include the scope required by the endpoint.

PermissionGrants Access To
read:declarationsGET /api/v1/declarations and GET /api/v1/declarations/:id
read:statisticsGET /api/v1/statistics
read:referenceReserved for future use. Reference and health endpoints are currently public and do not check this scope.

Rate Limiting

Every endpoint is rate-limited. Authenticated requests are counted per token; unauthenticated requests (e.g. health, reference) are counted per client IP. The default is 100 requests per 60 seconds; administrators can adjust both the limit and the window from the admin dashboard.

Response Headers

Every response — successful or rate-limited — includes the current rate-limit state:

HeaderDescription
X-RateLimit-LimitMaximum requests allowed in the current window.
X-RateLimit-RemainingRequests still available in the current window (never negative).
X-RateLimit-ResetUnix timestamp (seconds) when the current window ends and the counter resets.
Retry-AfterSent only on 429 responses. Number of seconds the client should wait before retrying.

Rate Limit Exceeded

When the limit is exceeded, the API returns 429 Too Many Requests with the RATE_LIMITED error code and the Retry-After header populated.

Endpoints

Health Check

GET/api/v1/health

Liveness probe. Public — no token required. Useful for uptime monitoring.

Response Example

{
  "data": {
    "status": "ok",
    "service": "travelpass-api",
    "version": "v1",
    "time": "2026-04-28T04:29:49Z"
  }
}

Error Responses

429Rate limit exceeded
503API_DISABLED — administrator has disabled the API

Declarations

GET/api/v1/declarationsRequires Auth

Paginated list of traveler declarations. By default returns only submitted declarations, ordered by submission time descending.

Required Permission: read:declarations

Parameters

NameTypeLocationDescription
pageintegerqueryPage number (default: 1)
limitintegerqueryItems per page (default: 25, max: 100)
statusstringqueryFilter by status: draft, submitted. Omit to return submitted only.
tripTypestringqueryarriving_visitor | arriving_resident | departing | transit
modeOfTravelstringqueryair | sea
fromDatedatequeryInclusive lower bound for submittedAt (ISO 8601 date or datetime).
toDatedatequeryInclusive upper bound for submittedAt (interpreted as end-of-day if a date-only value is given).

Response Example

{
  "data": [
    {
      "id": "f0ab05a1-eeb5-4792-8c20-882016e9ac9b",
      "referenceCode": "AS-ZX1W8P-2024",
      "status": "submitted",
      "submittedAt": "2024-12-13T10:15:00Z",
      "tripType": "departing",
      "modeOfTravel": "air",
      "personalInfo": {
        "firstName": "Sione",
        "middleName": "",
        "lastName": "Taufa",
        "passportNumber": "TO77889900",
        "citizenship": "Tonga",
        "dateOfBirth": "1975-12-03",
        "gender": "Male"
      },
      "travelDetails": {
        "airline": "Hawaiian Airlines",
        "flightNumber": "HA463",
        "departureDate": "2024-12-20"
      },
      "declarations": {
        "agricultureItems": false,
        "animals": false,
        "currency": false,
        "restrictedItems": false
      },
      "departureDetails": {
        "durationOfStay": { "value": 14, "unit": "days" },
        "spendingAmount": 2500,
        "purposeOfVisit": "visit_friends",
        "discoverySource": "word_of_mouth",
        "placeOfStay": "private",
        "islandsVisited": ["tutuila", "aunuu"],
        "mainAppeal": "culture"
      }
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 25,
    "total": 9,
    "totalPages": 1
  }
}

Error Responses

401UNAUTHORIZED — missing or invalid token
403FORBIDDEN — token lacks read:declarations
429Rate limit exceeded
503API_DISABLED
GET/api/v1/declarations/{id}Requires Auth

Retrieve a single declaration by either its UUID or its human-readable reference code (e.g. AS-ZX1W8P-2024).

Required Permission: read:declarations

Parameters

NameTypeLocationDescription
id*stringpathDeclaration UUID or reference code

Response Example

{
  "data": {
    "id": "f0ab05a1-eeb5-4792-8c20-882016e9ac9b",
    "referenceCode": "AS-ZX1W8P-2024",
    "status": "submitted",
    "submittedAt": "2024-12-13T10:15:00Z",
    "tripType": "departing",
    "modeOfTravel": "air",
    "personalInfo": { /* ... */ },
    "travelDetails":  { /* ... */ },
    "declarations":   { /* ... */ },
    "departureDetails": { /* ... */ }
  }
}

Error Responses

401UNAUTHORIZED
403FORBIDDEN
404NOT_FOUND — no declaration matches that id or reference code
429Rate limit exceeded

Statistics

GET/api/v1/statisticsRequires Auth

Aggregate counts across all submitted declarations: totals, breakdowns by trip type and mode of travel, plus the last-7-days submission count.

Required Permission: read:statistics

Response Example

{
  "data": {
    "total": 9,
    "byTripType": {
      "arriving_visitor": 4,
      "arriving_resident": 2,
      "departing": 2,
      "transit": 1
    },
    "byMode": {
      "air": 8,
      "sea": 1
    },
    "last7Days": 3
  }
}

Error Responses

401UNAUTHORIZED
403FORBIDDEN — token lacks read:statistics
429Rate limit exceeded

Reference Data

Reference endpoints expose the lookup vocabularies used elsewhere in the API (trip types, modes of travel, islands, etc.). They are public — no token required — but are still subject to rate limiting and the global API on/off switch.

GET/api/v1/reference

List every reference type with a short description and the number of values it contains.

Response Example

{
  "data": [
    { "type": "trip_types",                 "description": "Trip type options",                  "count": 4 },
    { "type": "modes_of_travel",            "description": "Mode of travel options",             "count": 2 },
    { "type": "islands",                    "description": "Islands of American Samoa",          "count": 7 },
    { "type": "discovery_sources",          "description": "How visitors discovered American Samoa", "count": 6 },
    { "type": "departure_purpose_of_visit", "description": "Departure purpose of visit options", "count": 8 },
    { "type": "place_of_stay",              "description": "Accommodation options",              "count": 2 },
    { "type": "main_appeal",                "description": "Main appeal factors for visitors",   "count": 4 }
  ]
}

Error Responses

429Rate limit exceeded
503API_DISABLED
GET/api/v1/reference/{type}

Retrieve the values for a specific reference type. Each value is an object with id (machine-readable code) and label (human-readable name).

Parameters

NameTypeLocationDescription
type*stringpathOne of: trip_types, modes_of_travel, islands, discovery_sources, departure_purpose_of_visit, place_of_stay, main_appeal

Response Example

{
  "data": [
    { "id": "tutuila",    "label": "Tutuila" },
    { "id": "aunuu",      "label": "Aunu'u" },
    { "id": "tau",        "label": "Ta'u" },
    { "id": "ofu",        "label": "Ofu" },
    { "id": "olosega",    "label": "Olosega" },
    { "id": "rose_atoll", "label": "Rose Atoll" },
    { "id": "swains",     "label": "Swains Island" }
  ]
}

Error Responses

404NOT_FOUND — unknown reference type
429Rate limit exceeded

Error Codes

All errors share the same envelope: a stable machine-readable code, a human-readable message, and — for validation failures — a details object naming the offending fields.

Error Response Format
{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Validation failed: Trip type is not included in the list",
    "details": { "trip_type": ["is not included in the list"] }
  }
}

Error Codes Reference

CodeHTTP StatusMeaning
VALIDATION_ERROR400 / 422Invalid request parameters or body. details lists the field errors.
UNAUTHORIZED401Missing bearer token, malformed header, or unknown token.
TOKEN_EXPIRED401Token is past its expires_at timestamp.
TOKEN_REVOKED401Token was revoked by an administrator.
FORBIDDEN403Token authenticated but lacks the required permission scope.
ENDPOINT_DISABLED403Administrator removed this endpoint from the allow-list.
NOT_FOUND404Requested resource (declaration, reference type) does not exist.
RATE_LIMITED429Rate limit exceeded. Honor the Retry-After header.
INTERNAL_ERROR500Unexpected server error. Safe to retry with backoff.
API_DISABLED503Administrator has flipped the global API kill-switch off.

SDK Examples

End-to-end examples for common integration patterns.

Fetch All Declarations with Pagination
async function fetchAllDeclarations(token) {
  const declarations = [];
  let page = 1;
  let totalPages = 1;
  const headers = { 'Authorization': `Bearer ${token}` };

  do {
    const res = await fetch(
      `/api/v1/declarations?page=${page}&limit=100`,
      { headers }
    );
    if (!res.ok) throw new Error(`HTTP ${res.status}`);
    const body = await res.json();
    declarations.push(...body.data);
    totalPages = body.pagination.totalPages;
    page += 1;
  } while (page <= totalPages);

  return declarations;
}
Handle Rate Limiting
async function fetchWithRetry(url, options, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const res = await fetch(url, options);
    if (res.status !== 429) return res;

    const retryAfter = parseInt(res.headers.get('Retry-After') || '60', 10);
    console.warn(`Rate limited; waiting ${retryAfter}s...`);
    await new Promise(r => setTimeout(r, retryAfter * 1000));
  }
  throw new Error('Max retries exceeded');
}
…