BerthTime API reference

The BerthTime API tracks ocean shipments by container number, bill of lading or booking number, across carriers, and returns one normalised timeline of DCSA-style events with vessel legs and ETAs. Add a reference with one call; BerthTime detects the reference type and the carrier, fetches the carrier data, keeps it up to date and pushes changes to your webhook.

Authentication

Send an API key as a bearer token: Authorization: Bearer bt_live_.... Company admins create keys in Settings > API keys; a key is shown once. Scopes are read and write, and the key's company is the tenant. Errors are RFC 7807 application/problem+json with a request_id; every response carries X-Request-Id and rate-limit headers (RateLimit-*); over the limit you get 429 with Retry-After.

Quick start

Track a container, bill of lading or booking

curl -X POST https://berthtime.com/api/v1/shipments \
  -H "Authorization: Bearer $BERTHTIME_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: po-4711" \
  -d '{"reference": "MSCU1234566", "customer_reference": "PO-4711"}'

ref_type and carrier (SCAC) are detected when omitted. A BL or booking brings in all its containers. Pass callback_url to get this shipment's updates pushed to that URL.

List shipments and read a timeline

curl https://berthtime.com/api/v1/shipments -H "Authorization: Bearer $BERTHTIME_API_KEY"
curl https://berthtime.com/api/v1/shipments/{shipment_id}/events -H "Authorization: Bearer $BERTHTIME_API_KEY"

Lists use cursor pagination (next_cursor → cursor). Reads only read stored data; they never call a carrier. POST /shipments/{shipment_id}/refresh asks for a new fetch (rate-limited per shipment).

Webhooks

Register an endpoint with POST /api/v1/webhooks. Every delivery is signed: BerthTime-Signature: t=<unix>,v1=<hex>, where v1 is HMAC-SHA256 of t + "." + body with the endpoint secret.

import hashlib, hmac

def verify(secret: str, header: str, body: bytes) -> bool:
    parts = dict(p.split("=", 1) for p in header.split(","))
    signed = parts["t"].encode() + b"." + body
    expected = hmac.new(secret.encode(), signed, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, parts["v1"])

Endpoints

Every public endpoint, grouped as in the interactive reference below. Base path /api/v1.

Meta

Health and service information.

get/api/v1/healthHealth check

Auth

Sign up, log in, log out, switch company (web session).

get/api/v1/auth/challengeProof-of-work challenge
post/api/v1/auth/signupCreate an account
post/api/v1/auth/loginLog in
post/api/v1/auth/logoutLog out
post/api/v1/auth/switch-companySwitch current company
post/api/v1/auth/forgot-passwordRequest a password reset email
post/api/v1/auth/reset-passwordSet a new password with an emailed token
post/api/v1/auth/verify-emailConfirm the email address with the emailed token
post/api/v1/auth/resend-verificationSend the verification email again
post/api/v1/auth/change-emailChange the sign-in email address
post/api/v1/auth/change-passwordChange the password

Account

The current principal.

get/api/v1/meWho am I
patch/api/v1/meUpdate my profile

Companies

Tenants, members, roles and invitations.

get/api/v1/companiesList my companies
post/api/v1/companiesCreate a company
get/api/v1/companies/{company_id}Get a company
patch/api/v1/companies/{company_id}Update a company
get/api/v1/companies/{company_id}/membersList members
patch/api/v1/companies/{company_id}/members/{member_id}Change a member's role
delete/api/v1/companies/{company_id}/members/{member_id}Remove a member
get/api/v1/companies/{company_id}/invitationsList invitations
post/api/v1/companies/{company_id}/invitationsInvite someone
delete/api/v1/companies/{company_id}/invitations/{invitation_id}Revoke an invitation
get/api/v1/invitations/{token}Preview an invitation (public)
post/api/v1/invitations/acceptAccept an invitation

API keys

Company API keys (bt_live_...), shown once.

get/api/v1/api-keysList API keys
post/api/v1/api-keysCreate an API key
delete/api/v1/api-keys/{key_id}Revoke an API key

Shipments

Tracked shipments, their containers and events.

get/api/v1/shipmentsList shipments
post/api/v1/shipmentsTrack a shipment (single or bulk)
get/api/v1/shipments/{shipment_id}Get a shipment
patch/api/v1/shipments/{shipment_id}Update a shipment
delete/api/v1/shipments/{shipment_id}Archive a shipment
post/api/v1/shipments/{shipment_id}/containersAdd container numbers
post/api/v1/shipments/{shipment_id}/refreshRefresh now
get/api/v1/shipments/{shipment_id}/eventsShipment timeline
get/api/v1/shipments/{shipment_id}/callback-deliveriesCallback delivery log

Sharing

Public read-only tracking links (/t/{token}): one per shipment, regenerate or disable any time.

get/api/v1/shipments/{shipment_id}/shareGet the share link
post/api/v1/shipments/{shipment_id}/shareCreate the share link (or return the active one)
patch/api/v1/shipments/{shipment_id}/shareChange share link options
delete/api/v1/shipments/{shipment_id}/shareDisable the share link
post/api/v1/shipments/{shipment_id}/share/regenerateRegenerate the share link
get/api/v1/public/track/{token}Public tracking page data

Reference data

Carrier coverage and reference detection.

get/api/v1/carriersCarrier coverage
post/api/v1/detectDetect reference type and carrier
get/api/v1/containers/{number}Look up a container

Webhooks

Push notifications for shipment changes.

get/api/v1/webhooks/callback-secretCompany callback signing secret
post/api/v1/webhooks/callback-secret/rotateRotate the company callback secret
get/api/v1/webhooksList webhook endpoints
post/api/v1/webhooksCreate a webhook endpoint
get/api/v1/webhooks/{webhook_id}Get a webhook endpoint
patch/api/v1/webhooks/{webhook_id}Update a webhook
delete/api/v1/webhooks/{webhook_id}Delete a webhook endpoint
post/api/v1/webhooks/{webhook_id}/testSend a test event
get/api/v1/webhooks/{webhook_id}/deliveriesDelivery log

Notifications

Email alert rules, digests, unsubscribe. Mail is sent from automation@berthtime.com.

get/api/v1/notification-rulesList alert rules
post/api/v1/notification-rulesCreate an alert rule
get/api/v1/notification-rules/{rule_id}Get an alert rule
patch/api/v1/notification-rules/{rule_id}Update an alert rule
delete/api/v1/notification-rules/{rule_id}Delete an alert rule
get/api/v1/email-typesEvery email BerthTime sends
get/api/v1/me/notification-preferencesMy notification preferences
patch/api/v1/me/notification-preferencesUpdate my notification preferences
put/api/v1/me/notification-preferences/rules/{rule_id}Opt in to / out of one alert rule
post/api/v1/me/notification-preferences/testSend me a test email
get/api/v1/unsubscribeWhat an unsubscribe link does (public)
post/api/v1/unsubscribeUnsubscribe (public, one-click)

Usage

Plan, billable shipments per month, overage.

get/api/v1/usagePlan and usage for a month
get/api/v1/usage/shipmentsShipments counted in a month

Billing

Stripe subscription: checkout, customer portal, webhook. 404 when billing is disabled.

get/api/v1/billingBilling status
post/api/v1/billing/checkoutStart a subscription
post/api/v1/billing/portalManage billing

Support

Contact form and support tickets (BT-1001): your company's requests, replies, close / reopen.

get/api/v1/support/ticketsYour company's support requests
post/api/v1/support/ticketsNew support request
get/api/v1/support/tickets/{ticket_id}A support request with its thread
post/api/v1/support/tickets/{ticket_id}/messagesReply
post/api/v1/support/tickets/{ticket_id}/closeClose (solved, no answer needed)
post/api/v1/support/tickets/{ticket_id}/reopenReopen
post/api/v1/public/contactContact form (creates a ticket)
get/api/v1/public/support/{token}Ticket thread from an emailed link
post/api/v1/public/support/{token}/messagesReply from an emailed link

Branding & portal

White-label branding, the customer tracking portal (/p/{slug}) and custom domains (Business and up; domains: Scale).

get/api/v1/brandingBranding and portal settings
put/api/v1/brandingUpdate branding and portal settings
post/api/v1/branding/logoUpload the logo
delete/api/v1/branding/logoRemove the logo
put/api/v1/branding/domainSet the custom portal domain
delete/api/v1/branding/domainRemove the custom domain
post/api/v1/branding/domain/verifyCheck the DNS records now
get/api/v1/public/portal/{slug}Customer portal (public)
post/api/v1/public/portal/{slug}/searchSearch a portal
get/api/v1/public/logo/{sha256}Logo image (public)
get/api/v1/public/portal-by-hostPortal slug for a custom domain
get/api/v1/public/domain-allowedCaddy on-demand TLS ask endpoint

MCP

Model Context Protocol server for AI assistants.

post/api/v1/mcpMCP server (Streamable HTTP)

Interactive reference

Try the endpoints below with your API key (Authorize).