Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions .github/workflows/publish_typescript_sdk.yml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
name: Publish TypeScript SDK

on:
release:
types: [published]

permissions:
contents: read

jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 18
registry-url: https://registry.npmjs.org/

- name: Read SDK version
id: sdk-version
run: |
node -e "const { version } = require('./sdks/typescript-sdk/package.json'); const fs = require('fs'); fs.appendFileSync(process.env.GITHUB_OUTPUT, `version=${version}\n`);"

- name: Check release tag matches SDK version
id: tag-check
run: |
tag="${{ github.event.release.tag_name }}"
version="${{ steps.sdk-version.outputs.version }}"
if [[ "$tag" == "v$version" || "$tag" == "sdk-v$version" || "$tag" == "$version" ]]; then
echo "publish=true" >> "$GITHUB_OUTPUT"
else
echo "publish=false" >> "$GITHUB_OUTPUT"
echo "Release tag $tag does not match SDK version $version; skipping publish."
fi

- name: Install dependencies
if: steps.tag-check.outputs.publish == 'true'
working-directory: sdks/typescript-sdk
run: npm ci

- name: Build
if: steps.tag-check.outputs.publish == 'true'
working-directory: sdks/typescript-sdk
run: npm run build

- name: Publish
if: steps.tag-check.outputs.publish == 'true'
working-directory: sdks/typescript-sdk
run: npm publish --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
3 changes: 3 additions & 0 deletions .gitignore
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
.DS_Store
.beads/

.env.local
.env.development.local

.tool-versions.local

.pytest_cache

node_modules
7 changes: 7 additions & 0 deletions docs/api-docs/getting-started/sdk-quickstart.mdx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
---
title: SDK Quickstart (TypeScript)
---

The SDK documentation has moved to the top-level SDK Docs section.

- [Open the SDK quickstart](/sdk/quickstart)
20 changes: 20 additions & 0 deletions docs/docs.json
Original file line numberDiff line numberDiff line change
Expand Up@@ -166,6 +166,26 @@
}
]
},
{
"tab": "SDK Docs",
"groups": [
{
"group": "Getting Started",
"pages": [
"sdk/introduction",
"sdk/quickstart"
]
},
{
"group": "Reference",
"pages": [
"sdk/methods",
"sdk/filtering-pagination",
"sdk/error-handling"
]
}
]
},
{
"tab": "DataSync",
"groups": [
Expand Down
70 changes: 70 additions & 0 deletions docs/sdk/error-handling.mdx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
---
title: "Error Handling"
description: "Catch and handle errors from the Terminal49 SDK"
---

The SDK throws typed errors you can catch and handle based on the error type.

## Error types

| Error | Cause |
|-------|-------|
| `AuthenticationError` | Invalid or missing API token |
| `AuthorizationError` | Valid token but insufficient permissions |
| `NotFoundError` | Resource doesn't exist or isn't accessible |
| `ValidationError` | Invalid request parameters |
| `RateLimitError` | Too many requests |
| `FeatureNotEnabledError` | Feature requires a plan upgrade |
| `UpstreamError` | Carrier or terminal API is unavailable |
| `Terminal49Error` | Generic error fallback |

## Basic error handling

```typescript
import {
Terminal49Client,
AuthenticationError,
RateLimitError,
NotFoundError,
} from '@terminal49/sdk';

const client = new Terminal49Client({
apiToken: process.env.T49_API_TOKEN!,
});

try {
await client.containers.get('container-uuid');
} catch (error) {
if (error instanceof AuthenticationError) {
console.error('Invalid API token');
} else if (error instanceof NotFoundError) {
console.error('Container not found');
} else if (error instanceof RateLimitError) {
console.error('Rate limited, retrying in 60s');
await new Promise((resolve) => setTimeout(resolve, 60000));
} else {
throw error;
}
}
```

## Automatic retries

The SDK automatically retries `429` and `5xx` responses with exponential backoff up to `maxRetries` (default: 2).

```typescript
const client = new Terminal49Client({
apiToken: process.env.T49_API_TOKEN!,
maxRetries: 3,
});
```

## Error properties

All SDK errors include:

| Property | Type | Description |
|----------|------|-------------|
| `message` | string | Human-readable error description |
| `status` | number | HTTP status code |
| `details` | unknown | Raw error payload from the API |
94 changes: 94 additions & 0 deletions docs/sdk/filtering-pagination.mdx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
---
title: "Filtering & Pagination"
description: "Query shipments and containers with filters and handle large result sets"
---

## Filtering shipments

Pass filter parameters to narrow results:

```typescript
const shipments = await client.shipments.list({
status: 'in_transit',
port: 'USLAX',
carrier: 'MAEU',
updatedAfter: '2025-01-01T00:00:00Z',
});
```

Available shipment filters:

| Filter | Type | Description |
|--------|------|-------------|
| `status` | string | Shipment status (for example `in_transit` or `delivered`) |
| `port` | string | UN/LOCODE for port of discharge |
| `carrier` | string | SCAC code (for example `MAEU`, `HLCU`) |
| `updatedAfter` | ISO 8601 | Only shipments updated after this timestamp |
| `includeContainers` | boolean | Set to `false` to omit containers from the included relationships |

## Filtering containers

```typescript
const containers = await client.containers.list({
status: 'discharged',
port: 'USLAX',
carrier: 'MAEU',
updatedAfter: '2025-01-01T00:00:00Z',
include: 'shipment,pod_terminal',
});
```

Available container filters:

| Filter | Type | Description |
|--------|------|-------------|
| `status` | string | Container status |
| `port` | string | UN/LOCODE for port of discharge |
| `carrier` | string | SCAC code |
| `updatedAfter` | ISO 8601 | Only containers updated after this timestamp |
| `include` | string | Comma-delimited list of related resources to include |

<Note>
For list endpoints, avoid heavy `include` usage for performance. When you need deep relationships, prefer single-resource endpoints like `containers.get` or `shipments.get`.
</Note>

## Pagination

List methods accept pagination options with `page` and `pageSize` (page numbers are 1-based):

```typescript
const page1 = await client.shipments.list({}, {
page: 1,
pageSize: 25,
format: 'mapped',
});

const page2 = await client.shipments.list({}, {
page: 2,
pageSize: 25,
format: 'mapped',
});
```

When using `format: 'mapped'`, list results include `items`, `links`, and `meta`. When using `format: 'raw'`, these live in the JSON:API response.

## Common patterns

### Recently updated shipments

```typescript
const oneDayAgo = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();

const updated = await client.shipments.list({
updatedAfter: oneDayAgo,
});
```

### In-transit containers at a specific port

```typescript
const containers = await client.containers.list({
status: 'in_transit',
port: 'USLAX',
});
```
55 changes: 55 additions & 0 deletions docs/sdk/introduction.mdx
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
---
title: "Introduction"
description: "TypeScript SDK for the Terminal49 API"
---

The Terminal49 TypeScript SDK lets you track containers, retrieve shipment data, and receive status updates from your Node.js applications.

## Requirements

- Node.js 18 or later
- A Terminal49 API key ([get one here](https://app.terminal49.com/developers))

## Install

```bash
npm install @terminal49/sdk
```

## Setup

Store your API key as an environment variable:

```bash
export T49_API_TOKEN=your_api_key
```

Then initialize the client:

```typescript
import { Terminal49Client } from '@terminal49/sdk';

const client = new Terminal49Client({
apiToken: process.env.T49_API_TOKEN!,
});
```

## What you can do

- **Track containers** — Create tracking requests by container number, booking number, or bill of lading
- **List shipments and containers** — Query with filters by status, port, carrier, or date
- **Get transport events** — Pull milestones, timestamps, and location updates
- **Fetch routing details** — See the full journey including vessels and ports

For real-time updates, set up [webhooks](/api-docs/in-depth-guides/webhooks) to receive status changes as they happen.

## Next steps

<CardGroup cols={2}>
<Card title="Quickstart" icon="rocket" href="/sdk/quickstart">
Track your first container in 5 minutes
</Card>
<Card title="Methods Reference" icon="code" href="/sdk/methods">
See all available SDK methods
</Card>
</CardGroup>
Loading
Loading