diff --git a/.cursor/rules/core.mdc b/.cursor/rules/core.mdc new file mode 100644 index 00000000..545144e3 --- /dev/null +++ b/.cursor/rules/core.mdc @@ -0,0 +1,62 @@ +--- +description: +globs: +alwaysApply: true +--- +# General Principles + +### Accuracy and Relevance + +- Systematically ensure that all generated responses directly align with user requests. +- Thoroughly understand, gather, or validate relevant context using appropriate built-in tools before responding. + +### Validation Over Modification + +- Prioritize extensive validation and comprehensive understanding of existing code and its operational context before introducing alterations or creating new artifacts. + +### Safety-First Execution + +- Perform in-depth analyses of dependencies and interconnected components. +- Achieve comprehensive comprehension of workflows and meticulously evaluate potential risks before implementing modifications. +- Maintain clear and proactive communication regarding identified risks, dependencies, or implications. + +### User Intent Comprehension + +- Employ analytical methods to explicitly and accurately confirm user intentions. +- Evaluate context and tools rigorously to ensure precise fulfillment of user requirements. +- Continuously validate adherence to user-defined or inferred objectives. + +### Mandatory Validation Protocol + +- Enforce rigorous validation, verification, and double-checking as fundamental operational practices. +- Prioritize validation to ensure accuracy, prevent errors, and maintain a consistently high standard of quality assurance. + +### Reusability Mindset + +- Foster a strategic orientation towards reusability in decision-making and operational processes. +- Proactively utilize built-in tools (`codebase_search`, `grep_search`, and the command `tree -L {depth} | cat`) to identify and leverage pre-existing solutions. +- Advocate for reusability to enhance consistency, minimize redundancy, and improve maintainability. + +### Contextual Integrity and Documentation + +- Do not rely solely on user-generated documentation (e.g., README files, inline documentation) as authoritative sources. +- Use documentation primarily as supplementary context or as a validation and verification resource. + +# Tool and Behavioral Guidelines + +### Rigorous Path Validation for File Operations + +- Conduct repetitive and thorough validation of file paths using commands such as `pwd` and `tree -L {depth} | cat` to ensure correctness. +- Explicitly confirm the working directory and clearly specify the `target_file` attribute as the file path relative to the workspace root for all file edits and creations. +- Evaluate potential reusable resources before initiating file operations. + +### Systematic Use of the `tree -L {depth} | cat` Command + +- Regularly use the `tree -L {depth} | cat` command for a comprehensive visualization of directory structures. +- Critically analyze the visualized structure to identify existing reusable resources, ensuring accurate and deliberate placement of file operations. + +### Mandatory Replacement of `read_file` Tool with Terminal Command + +- Never utilize the built-in `read_file` tool under any circumstances. +- Always use the terminal command `cat ` to read file contents. +- Conduct detailed analyses of file content via the terminal command to determine reusability, dependencies, and logical structures, ensuring thorough context acquisition before making modifications. diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 00000000..dcf61b7d --- /dev/null +++ b/.tool-versions @@ -0,0 +1 @@ +nodejs 22.2.0 diff --git a/API-Documentation-Improvement-Tracker.md b/API-Documentation-Improvement-Tracker.md new file mode 100644 index 00000000..8cf79469 --- /dev/null +++ b/API-Documentation-Improvement-Tracker.md @@ -0,0 +1,68 @@ +# Terminal49 API Documentation Improvement Tracker + +## Progress Overview + +| Category | Status | Completion % | +|----------|--------|--------------| +| Getting Started | In Progress | 75% | +| In-Depth Guides | In Progress | 60% | +| API Reference Examples | In Progress | 25% | +| Tutorials | Not Started | 0% | +| General Documentation | In Progress | 40% | + +## Pages Changed/Added + +| Page | Type | Description of Changes | +|------|------|------------------------| +| `docs/api-docs/getting-started/start-here.mdx` | Updated | Improved introduction, clarified setup process, added more context about the API architecture | +| `docs/api-docs/getting-started/capabilities-overview.mdx` | Updated | Enhanced capabilities listing, added examples of what can be accomplished with the API | +| `docs/api-docs/in-depth-guides/json-api-guide.mdx` | New | Created comprehensive guide explaining JSON:API concepts, structure, and how Terminal49 implements the specification | +| `docs/api-docs/in-depth-guides/polling-vs-webhooks.mdx` | New | Added comparison guide detailing the differences, advantages, and use cases for polling vs webhooks | +| `docs/api-docs/in-depth-guides/webhooks.mdx` | New | Created detailed guide for webhook implementation including endpoint setup, registration, testing, and management | +| `docs/api-docs/in-depth-guides/authentication.mdx` | New | Created comprehensive authentication guide with API key management, examples in multiple languages, security best practices | +| `docs/api-docs/api-reference/tracking-requests/create-a-tracking-request.mdx` | Updated | Enhanced documentation with clearer examples, added business use cases, improved response explanations | +| `docs/docs.json` | Updated | Updated navigation structure to include new guides and improve organization | + +## Completed Updates + +- [x] Improved introduction and setup guidance in `docs/api-docs/getting-started/start-here.mdx` +- [x] Enhanced capabilities overview in `docs/api-docs/getting-started/capabilities-overview.mdx` +- [x] Created JSON:API guide in `docs/api-docs/in-depth-guides/json-api-guide.mdx` +- [x] Updated tracking request documentation in `docs/api-docs/api-reference/tracking-requests/create-a-tracking-request.mdx` +- [x] Created "Polling vs. Webhooks" comparison guide in `docs/api-docs/in-depth-guides/polling-vs-webhooks.mdx` +- [x] Created Webhook implementation guide in `docs/api-docs/in-depth-guides/webhooks.mdx` +- [x] Created Authentication guide in `docs/api-docs/in-depth-guides/authentication.mdx` +- [x] Fixed MDX parsing errors to ensure compatibility with Mintlify + +## In Progress + +- [ ] Updating webhook API reference documentation +- [ ] Creating Terminal49 data model guide +- [ ] Improving error handling documentation + +## Next Steps + +1. **Immediate Focus (Next 48 hours):** + - Develop error handling and troubleshooting documentation + - Add examples in multiple programming languages for remaining endpoints + +2. **Medium Priority (Next Week):** + - Enhance all endpoint documentation with consistent format and examples + - Create step-by-step tutorials for common use cases + - Add more detailed error code documentation + +3. **Future Improvements:** + - Add interactive API explorer + - Create video tutorials for complex integration scenarios + - Develop SDK documentation + +## Notes on Implementation + +Our approach focuses on: +1. Ensuring consistent format and style across all documentation +2. Adding practical examples that demonstrate real-world usage +3. Improving navigability to help users find relevant information quickly +4. Following JSON:API specification accurately in our examples +5. Providing clear guidance on webhooks vs. polling approaches + +All documentation is being developed to align with OpenAPI specifications and Mintlify standards to ensure compatibility with the documentation portal. diff --git a/Documentation-improvement-plan.md b/Documentation-improvement-plan.md new file mode 100644 index 00000000..4aef2ac6 --- /dev/null +++ b/Documentation-improvement-plan.md @@ -0,0 +1,331 @@ +Below is a sequential, step-by-step plan for improving Terminal49’s API documentation, along with suggested content additions. Each step is described in practical terms, so a developer or an LLM can parse and follow the instructions easily. + + +## Plan Progress tracking for AI Agent +Use this area to create a plan checklist and work through the plan + +- [ ] step 1 +- [ ] step 2 + + +⸻ + +Step 1: Conduct a Documentation Audit + +What to do: + 1. Review all existing pages – Identify incomplete or unclear endpoint references, broken links, missing parameters, or unaddressed error responses. + 2. Gather feedback or known user issues – Collect any existing support tickets, GitHub issues, or user comments about the docs. + 3. Compile a gap report – List each endpoint or topic where information is missing or unclear. + +Why this step matters: + • Ensures we have a complete view of what’s working and what needs improvement. + • Prevents duplicating content or missing critical fixes. + +Example content addition: + • “We found that the GET /containers documentation has no example for a container with a customs hold. We also discovered the ‘Webhook Retries’ page references an old link. We’ll address both.” + +⸻ + +Step 2: Update and Expand Endpoint Documentation + +What to do: + 1. Add multi-scenario examples – Show how each endpoint behaves under different conditions (e.g., container delayed, container cleared, container picked up). + 2. Provide request and response pairs – For each endpoint, include at least one full request body and the corresponding JSON response. + 3. Explain key fields in plain language – Add short, friendly definitions (e.g., “last_free_day = The final date to pick up the container without incurring storage fees”). + 4. Document all HTTP status codes – Include typical success (200/201) responses, as well as error codes (e.g., 400, 422, 500). + +Why this step matters: + • Saves developers and non-developers time by showing them exactly what data to expect. + • Reduces guesswork about how the API handles edge cases. + +Example content addition: + +### GET /containers/:id + +**Purpose:** Retrieve details of a specific container. + +**Example Request:** +```bash +GET /containers/ABC123 +Authorization: Bearer + +Example Response (Container with Customs Hold): + +{ + "data": { + "id": "ABC123", + "type": "container", + "attributes": { + "status": "on_hold", + "hold_reason": "Customs Inspection", + "last_free_day": "2025-03-25", + "pickup_location": "Port of Oakland Terminal 3", + ... + } + } +} + +Notes: + • status can be in_transit, arrived, on_hold, or picked_up. + • hold_reason is returned only if status is on_hold. + • last_free_day indicates the last day to avoid demurrage charges. + +--- + +## Step 3: Create a “Capabilities Overview” Page + +**What to do:** +1. **Summarize Terminal49’s functionality** – Provide a high-level, non-technical description of major features (tracking ocean shipments, retrieving events, data standardization). +2. **Highlight key endpoints** – Offer a quick link or table referencing the main endpoints (e.g., Tracking Requests, Containers, Shipping Lines). +3. **Explain benefits in business terms** – Emphasize how this data helps reduce manual tracking, avoid fees, and automate notifications. + +**Why this step matters:** +- Non-developers (like operations managers) can understand the scope of what Terminal49 offers. +- Helps new users grasp the bigger picture before diving into endpoint specifics. + +**Example content addition:** +```markdown +# Capabilities Overview + +Terminal49’s API provides end-to-end container tracking and logistics data. Key capabilities include: + +- **Real-Time Event Notifications**: Get alerted on arrivals, holds, vessel departures, and more. +- **Automated Updates**: Standardized data from multiple carriers, terminals, and rail lines. +- **Reference Data**: Access shipping lines, ports, and terminal information without manual lookups. + +**Why It Matters** +- **Reduce Manual Work**: Eliminate the need for manual carrier websites and phone calls. +- **Prevent Extra Fees**: Track Last Free Day changes in real time to avoid demurrage charges. +- **Improve Visibility**: Combine container statuses from multiple carriers in one place. + + + +⸻ + +Step 4: Develop a “Quick Start” Onboarding Guide + +What to do: + 1. Focus on event-driven architecture – Introduce webhooks early, explaining how to register them and why they are more efficient than polling. + 2. Show a minimal end-to-end example – Include code for creating a Tracking Request, receiving a webhook event, and verifying the data. + 3. Provide test numbers – Show how to simulate a real workflow with test SCAC or container IDs. + +Why this step matters: + • Gives beginners a fast path to seeing how data flows from Terminal49 to their system. + • Encourages best practices (i.e., webhooks) from the start. + +Example content addition: + +# Quick Start Guide + +## 1. Get Your API Key +Sign up on Terminal49, navigate to your account settings, and copy your `API_KEY`. + +## 2. Create Your First Tracking Request +Send a POST request to `/tracking_requests`: +```bash +curl -X POST https://api.terminal49.com/tracking_requests \ + -H "Authorization: Bearer " \ + -d '{ + "data": { + "type": "tracking_request", + "attributes": { + "scac": "TEST", + "container_number": "TEST-TR-SUCCEEDED" + } + } + }' + +This will initiate a track-and-trace request. Using "TEST-TR-SUCCEEDED" ensures a successful mock response. + +3. Set Up a Webhook + +Register your webhook endpoint: + +curl -X POST https://api.terminal49.com/webhooks \ + -H "Authorization: Bearer " \ + -d '{ + "data": { + "type": "webhook", + "attributes": { + "url": "https://yourdomain.com/t49-webhook", + "event_types": ["tracking_request.succeeded", "container.arrived"] + } + } + }' + +4. Verify the Webhook Event + +You will receive a JSON POST at https://yourdomain.com/t49-webhook once the container status updates. Now you’re all set! + +--- + +## Step 5: Add a “Polling vs. Webhooks” Section + +**What to do:** +1. **Explain both approaches** – Show how to set up a periodic poll, then highlight the advantages of webhooks (lower overhead, real-time updates). +2. **Discuss trade-offs** – Provide examples of when polling might be acceptable (simple prototypes, no real-time requirement). + +**Why this step matters:** +- Clarifies best practice for new integrators. +- Reduces confusion about which model to implement for production systems. + +**Example content addition:** +```markdown +# Polling vs. Webhooks + +## Polling +- **How It Works**: Your system calls Terminal49’s API at regular intervals for updates. +- **Pros**: Simple to implement initially. +- **Cons**: Risk of missing urgent events, higher API usage, potential rate-limit issues. + +## Webhooks +- **How It Works**: Terminal49 sends updates to your registered endpoint in real time. +- **Pros**: Reduced overhead, immediate notifications, best for large-scale or time-sensitive operations. +- **Cons**: Requires setting up and maintaining a secure, publicly accessible endpoint. + + + +⸻ + +Step 6: Create Tutorials (Hands-On Guides) + +What to do: + 1. Implement short, focused tutorials – E.g., Building a “Where’s My Container?” Dashboard, Sending Delay Alerts via Email, etc. + 2. Show code in common languages – Provide examples in Python, Node.js, or another popular stack. + 3. Add screenshots or diagrams – Visuals help non-technical team members and managers follow along. + +Why this step matters: + • Offers real-world use cases that demonstrate how to stitch endpoints together. + • Helps new users quickly build something tangible. + +Example content addition: + +# Tutorial: Building a “Where’s My Container?” Dashboard + +In this tutorial, you’ll: +1. Create tracking requests. +2. Fetch container statuses. +3. Display status updates on a simple web page. + +## Prerequisites +- Node.js installed +- Basic HTML knowledge + +... + +## Step 3: Fetch Container Status +Use this endpoint in your Node.js backend: +```js +const axios = require('axios'); + +async function getContainer(containerId, apiKey) { + const resp = await axios.get( + `https://api.terminal49.com/containers/${containerId}`, + { headers: { Authorization: `Bearer ${apiKey}` } } + ); + return resp.data; +} + +… + +--- + +## Step 7: Add Industry-Specific Guides + +**What to do:** +1. **Create pages targeting different verticals** – Freight forwarders, importers, 3PLs, trucking companies, etc. +2. **Highlight relevant endpoints or workflows** – E.g., *How a freight forwarder can bulk-track multiple containers*, *How drayage companies use `last_free_day` to optimize pickups*. +3. **Use minimal code, more workflow diagrams** – Because these guides often serve operations managers or non-developers. + +**Why this step matters:** +- Demonstrates how to leverage the API’s features for specific business goals. +- Bridges the knowledge gap for less technical readers. + +**Example content addition:** +```markdown +# Terminal49 for Freight Forwarders + +**Common Use Cases**: +- Consolidate multiple container statuses in your TMS. +- Track containers across different carriers and terminals. + +**Key Endpoints**: +- `POST /tracking_requests`: Bulk create tracking for many containers at once. +- `GET /containers`: Retrieve statuses of all active shipments. + +**Recommended Workflow**: +1. Import your container list into Terminal49 via `/tracking_requests`. +2. Receive real-time updates through your custom webhook. +3. Surface any exceptions (e.g., holds, delays) in your TMS or an internal dashboard. + + + +⸻ + +Step 8: Introduce FAQ & Troubleshooting Pages + +What to do: + 1. Aggregate common questions – E.g., “Why is my container not updating?”, “How do I handle a 422 error?” + 2. Provide immediate answers – Link to relevant documentation sections if needed. + 3. Encourage user contributions – Let users submit new questions or solutions via GitHub issues or support emails. + +Why this step matters: + • Reduces repetitive support queries. + • Offers quick fixes for frequent integration issues. + +Example content addition: + +# FAQ & Troubleshooting + +### Q: Why is my container not updating in real-time? +**A:** Make sure you have a webhook registered. Polling for updates might cause delays. See the [Polling vs. Webhooks](/docs/polling-vs-webhooks) page. + +### Q: I received a 422 error when creating a tracking request. What does this mean? +**A:** Typically, this indicates a duplicate request or invalid container number. Double-check the container ID and SCAC. See [Tracking Request Errors](/docs/tracking-requests#errors). + + + +⸻ + +Step 9: Refine Structure & Navigation + +What to do: + 1. Group related content – Ensure “Getting Started,” “API Reference,” “Tutorials,” “Guides,” and “FAQ” each have intuitive sections. + 2. Add clear top-level navigation – For example: + • Overview + • Getting Started (Quick Start + Webhook Setup + FAQ) + • Reference (Endpoints) + • Tutorials + • Use Case Guides + 3. Cross-link – Insert “See also” links in relevant pages to unify the documentation. + +Why this step matters: + • Streamlines the user journey so they can quickly find relevant material. + • Encourages discovery of advanced features or best practices. + +Example content addition: + • “At the end of the Quick Start Guide, add a link: ‘Want to learn more? Check out our Building an Alert System tutorial.’” + +⸻ + +Step 10: Gather Feedback & Iterate + +What to do: + 1. Solicit user input – Encourage readers to open GitHub issues or submit feedback forms. + 2. Monitor analytics – Track page views, time on page, and popular search terms to see if users find what they need. + 3. Make incremental improvements – Update docs regularly based on feedback, new features, or changes in the API. + +Why this step matters: + • Keeps the documentation “alive” and responsive to user needs. + • Avoids stagnation as the API evolves or new best practices emerge. + +Example content addition: + • “Add a footer note: ‘Have questions or suggestions? Submit feedback here.’” + +⸻ + +Final Thoughts + +This 10-step plan provides a systematic way to audit, enhance, and expand Terminal49’s API documentation. By creating richer endpoint references, tailored onboarding, real-world tutorials, and industry-specific guides, you’ll ensure both technical and non-technical users can harness the full power of your platform. + +Each step includes a sample of the content to add or revise, making it easy for a developer or an LLM to understand and implement. By following these steps in order, you’ll continuously improve your documentation without overwhelming users or maintainers. \ No newline at end of file diff --git a/capabilities-overview.mdx b/capabilities-overview.mdx new file mode 100644 index 00000000..a439e077 --- /dev/null +++ b/capabilities-overview.mdx @@ -0,0 +1,181 @@ +--- +title: Terminal49 API Capabilities Overview +description: A comprehensive overview of all features and capabilities available through the Terminal49 API. +--- + +# Terminal49 API Capabilities + +Terminal49's API provides end-to-end ocean freight visibility through a collection of endpoints that deliver real-time tracking data, reference information, and event notifications. This overview will help you understand what's possible and how each feature fits into your logistics workflows. + +## Core Capabilities + + + + Track containers and shipments across their entire journey using bill of lading, booking, or container numbers. + + + Receive real-time updates via webhooks when shipment status changes, from origin to destination. + + + Access standardized data about carriers, ports, terminals, and more for consistent reporting. + + + Get comprehensive container information including status, location, free time, and holds. + + + +## End-to-End Visibility + +Terminal49 provides visibility across the entire container journey: + + + + Track empty container pickup, loading, and departure from origin port. + + + Follow vessel movements and receive ETA updates throughout the journey. + + + Get notified when the vessel arrives, berths, and when containers are discharged. + + + Monitor container availability, customs status, holds, and last free day information. + + + Track rail departures, arrivals, and final delivery events to inland destinations. + + + +## Key Differentiators + + + + Terminal49 standardizes data from different carriers into a single consistent format. For example, each carrier might report a container discharge event differently, but Terminal49 normalizes these into the same event type (`container.transport.vessel_discharged`) with standardized timestamps and location formats. + + **Business Benefit**: Build once, track across all carriers without custom integration for each shipping line. + + + + Instead of constantly polling for updates, Terminal49 notifies you via webhooks when meaningful events occur. This includes status changes, ETA updates, terminal holds, and changes to last free day. + + **Business Benefit**: Respond immediately to delays, holds, or availability changes, reducing demurrage costs and improving planning. + + + + Terminal49 actively monitors and notifies you of changes to Last Free Day (LFD) - the date by which containers must be picked up to avoid storage fees. This data is often buried in terminal websites and subject to change. + + **Business Benefit**: Avoid costly demurrage fees by prioritizing pickups based on accurate LFD information. + + + + The API provides comprehensive information about holds (customs, freight, terminal) and other exceptions that may delay pickup. + + **Business Benefit**: Proactively address holds and plan alternative shipments when delays occur. + + + +## How It Works + + + ![Terminal49 API Flow Diagram](/images/api-flow-diagram.png) + + +1. **Submit tracking requests** using bill of lading, booking, or container numbers +2. **Terminal49 monitors** these shipments by aggregating data from multiple sources +3. **Your systems receive updates** through webhooks when any change occurs +4. **Query additional information** as needed using the REST API endpoints + +## Available Data Points + +Here are key data points available through the Terminal49 API: + + + + - Bill of lading numbers + - Booking numbers + - Port of loading/discharge + - Shipper/consignee information + - Vessel assignments + - Estimated dates (departure, arrival) + - Associated containers + + + + - Container numbers and size/type + - Current location and status + - Last free day information + - Terminal availability + - Customs/terminal/freight holds + - Transport events history + - Pickup appointments + + + + - Vessel departures and arrivals + - Container loading/discharge + - Customs and terminal holds + - Availability changes + - Last free day updates + - Rail movements + - Terminal cutoffs + + + + - Shipping lines (with SCAC codes) + - Terminals and ports + - Vessels and voyages + - Metro areas + - Standardized location formats + + + +## Common Use Cases + + + + Connect Terminal49 to your Transportation Management System for real-time visibility without manual tracking. + + + + Build automated alerts to notify customers about shipment status changes, delays, or container availability. + + + + Prioritize container pickups based on last free day information to avoid demurrage charges. + + + + Identify and resolve customs holds, freight issues, or delays that require intervention. + + + + Build real-time dashboards showing shipment status across your supply chain. + + + + Plan warehouse staffing and inventory based on accurate container arrival predictions. + + + +## Getting Started + +Ready to start integrating with Terminal49? Here's how to begin: + + + + [Sign up for Terminal49](https://app.terminal49.com/signup) and get your API key from the [developer portal](https://app.terminal49.com/developers/api-keys). + + + Follow our [quick start guide](/api-docs/getting-started/tracking-shipments-and-containers) to submit your first tracking request. + + + Configure [webhooks](/api-docs/in-depth-guides/webhooks) to receive real-time updates about your shipments. + + + Browse our [API reference](/api-docs/api-reference) to discover all available endpoints. + + + + + Our team is here to help you get the most out of Terminal49. Contact us at [support@terminal49.com](mailto:support@terminal49.com) with any questions about our API capabilities. + diff --git a/docs-improvement-research.md b/docs-improvement-research.md new file mode 100644 index 00000000..59637eab --- /dev/null +++ b/docs-improvement-research.md @@ -0,0 +1,59 @@ +Strategic Improvement Plan for Terminal49 API Documentation + +Enhance Endpoint Documentation with Comprehensive Examples + +Each API endpoint should have clear, multi-scenario examples to guide users. Currently, the documentation provides basic JSON payload samples (often truncated for brevity) , but it can be enriched with additional examples covering different states and outcomes. We will: + • Add multiple example responses for various states – For instance, a Shipment endpoint could show a container in transit, one that has arrived with holds, and one after pickup. This gives users insight into how fields like status, holds, or dates appear in different scenarios. Similarly, for Tracking Requests, include examples for a pending request, a succeeded tracking (with linked shipment created), and a failed attempt (with error information). + • Include request and response pairs – Show the request payload and the full JSON response for each example. This helps developers and non-developers see the input-output clearly. For example, demonstrating a tracking_requests POST and the resulting shipment data (including what a “pending” vs “active” tracking looks like) makes the process concrete. + • Ensure consistency and clarity – Use realistic data in examples (e.g. sample BOL numbers, SCACs, timestamps) that reflect real-world values. Avoid heavy jargon in field descriptions; instead, add brief comments or notes explaining what key fields mean in plain language (e.g. “last_free_day: Date by which container must be picked up to avoid storage fees”). This caters to logistics operators and management who may not be familiar with raw JSON details. + +Providing richer examples will make the API reference more self-explanatory. Users can quickly grasp how data changes with different conditions, reducing trial-and-error during integration. It also benefits non-developers by illustrating outcomes in an accessible way, rather than abstract descriptions. + +Expand Education on All API Capabilities + +Beyond the endpoint specs, the documentation should teach users about Terminal49’s full feature set and how to use it effectively. Currently, the docs touch on some use cases (e.g. highlighting data points like Last Free Day or fees and their use cases ), but we can broaden this educational content. Our plan: + • Comprehensive “Capabilities Overview” – Introduce a section that summarizes everything the API can do in layman’s terms. For example: “Terminal49 can track ocean shipments end-to-end, including vessel events, terminal events, rail movements, and customs holds. It also provides reference data (ports, shipping lines) and value-added data like standardized location names.” This overview should be written for a non-technical audience (heads of operations, management) to understand the possibilities without diving into endpoints. + • Deep-dive guides for specific features – Expand the existing guides to cover all important capabilities. For instance, create easy-to-follow explanations for things like Raw vs. Transport Events (what they are and when to use each), JSON API includes (using the include parameter to fetch related objects, building on the “Including Resources” guide), and Data normalization (how Terminal49 standardizes carrier data). Each guide should be scenario-driven and avoid unnecessary technical jargon. If terms like “JSONAPI” or “SCAC” are used, we’ll add a one-line explanation for clarity (e.g. “SCAC – a standardized carrier code ”). + • Visual aids and diagrams – Where helpful, include diagrams or flowcharts. For example, a diagram of the Tracking Request Lifecycle could illustrate the steps from request submission to various outcomes (succeeded, failed, awaiting manifest, etc.) and the creation of shipments/containers. The docs already reference a “Tracking Request Diagram” (which appears to be a placeholder) – we will create a clear diagram for it, making the event flow easier to digest  . Visual explanations will help IT leaders and operations managers quickly grasp processes without parsing text-heavy descriptions. + • Highlight lesser-known endpoints – Ensure that endpoints for ancillary data (like Shipping Lines, Ports, Terminals) are not just listed, but put into context. We might add notes like: “Use the Get Port by LOCODE endpoint to translate location codes into human-readable port names – useful for reporting.” This educates users that the API can serve as a single source of truth for reference data, not just tracking events. + +By broadening the educational material, every stakeholder – from a developer to a logistics manager – can learn what the API offers. The tone will remain educational and straightforward, focusing on “what you can do” with Terminal49 and “how to do it” in simple terms. This empowers users to take full advantage of all features, not just the basic tracking requests. + +Improve Onboarding and Emphasize Event-Driven Integration + +A smooth onboarding experience is crucial. We will create a clear getting-started path that guides users through initial setup and strongly encourages the preferred event-driven model using webhooks. The current documentation does mention that Terminal49 is an “event-based API” and that webhooks are more efficient than polling  , but we can make this guidance more prominent and actionable: + • Step-by-step onboarding guide – Combine and streamline the “Start Here” and “Quick Start” materials into a cohesive onboarding tutorial. This guide will walk a new user through obtaining API keys, creating a first tracking request, and setting up a webhook endpoint to receive updates. Instead of treating webhook setup as an optional or later step, we’ll integrate it early in the process. For example: after the user creates a tracking request (via a quickstart example), the next step in the guide is to register a webhook and see a sample notification. This reinforces best practices from the start. + • Emphasize webhooks (with explanations) – Clearly explain why an event-driven approach is recommended. We’ll add a short comparison: polling (periodically calling the API for updates) vs. webhooks (receiving push notifications). Explain that polling is simpler to implement initially but can miss real-time updates and put load on your systems, whereas webhooks deliver instant notifications and are more efficient . We will keep this non-judgmental and educational, e.g. “For testing or simple integrations you might poll the API, but in production we strongly encourage webhooks to ensure you don’t miss time-sensitive updates like terminal holds or ETA changes.” By outlining the trade-offs, IT leaders can make informed decisions and are more likely to invest in a webhook listener from the beginning. + • Webhook setup guidance – Provide code snippets and tools to lower the barrier to using webhooks. The docs already include a JavaScript snippet for receiving a webhook POST  ; we can expand on this with a minimal complete example (perhaps a simple express.js or Flask server snippet) that prints incoming events. Also, clarify webhook security best practices (e.g., validating payloads or using secret tokens) in simple terms. This gives developers a head start in implementing robust webhook consumers. + • Use of test events for onboarding – Incorporate Terminal49’s Test Numbers feature into onboarding. We will encourage users to try a test tracking number (with SCAC “TEST”) as their first tracking request, which triggers a predictable webhook event . For example, the guide can instruct: “Try tracking TEST-TR-SUCCEEDED with SCAC TEST – this will always generate a tracking_request.succeeded event to your webhook, so you can verify your endpoint is working.” By doing this, users get immediate feedback in their system (a fake shipment and a webhook call) without waiting for a real shipment update. This hands-on approach both teaches the event cycle and builds confidence that the integration is working as intended. + +Overall, the onboarding will be reworked to front-load the knowledge that Terminal49 excels as an event-driven system. By the end of the first tutorial, the user should have: an API key set up, a tracking request made, and a webhook that has received a sample notification. This clear path sets all types of users (developers and operations teams alike) on the right track from day one. + +Introduce Tutorials and Industry-Specific Integration Guides + +To cater to a broad audience and various use cases, we will add new documentation sections: Tutorials for common integration tasks, and Industry-Specific Guides for contextual best practices. These additions address the needs of both hands-on developers and decision-makers looking for tailored solutions. + • Step-by-step tutorials – These will be practical guides that walk through building something with the API. For example: “Building a Shipment Tracker in 30 Minutes” could guide a developer through retrieving a list of shipments and displaying status updates in a simple web app. Another tutorial might be “Sending Automatic Email Alerts for Delays”, showing how to combine webhooks and a notification service. The idea is to provide cookbook-style examples that are easy to follow. Such tutorials will use simple language and assume minimal prior knowledge, making them friendly for new developers or IT generalists. They’ll also implicitly teach API usage (e.g., how to paginate through shipments, how to filter events) in an applied manner. + • Language-specific quickstart examples – To further help developers, we can include brief guides or code samples for popular languages (Python, JavaScript, etc.). For instance, a Python script example for creating a tracking request and printing the result, or a Node.js example for setting up a webhook receiver. These can be separate pages or an appendix in the tutorials section. The tone remains educational, with clear comments, so even non-developers reading it can grasp what the code is doing. + • Industry-specific guides – Create documentation pages that speak directly to the use cases of different roles or sectors in logistics. For example: “Integrating Terminal49 for Freight Forwarders” can outline how a freight forwarder’s operations team might use the API to feed their Transportation Management System, emphasizing features like consolidating multiple carrier updates into one feed. “Terminal49 for Drayage and Trucking Companies” might focus on using Last Free Day and availability data to optimize dispatch and reduce fees. “Visibility for Retail Supply Chains” could show how an importer’s head of operations can get proactive alerts on delays. These guides will be written in mostly non-technical terms (with minimal code), focusing on workflow integration and ROI of the API data. We will include diagrams or flowcharts relevant to each use case, and small code/config snippets as needed (e.g., an example of a webhook payload for a container availability event in the drayage guide). + • Embed real-world examples and case studies – Where possible, include brief case-study style anecdotes in these guides (e.g., “A freight forwarder used Terminal49 to reduce manual track & trace work by 50%. Here’s how you can achieve that via the API’s consolidated tracking.”). This makes the documentation engaging for management-level readers and shows the practical impact of integration. + +By adding tutorials and specialized guides, we ensure that the documentation isn’t one-size-fits-all. Developers get hands-on guidance to shorten their learning curve, and logistics professionals see clear connections between the API features and their business needs. The tone in these sections will remain approachable and solution-focused, avoiding unnecessary technical depth unless required. + +Address Documentation Gaps and Continuous Improvements + +Before any radical restructuring, we will methodically address gaps in the current docs and make incremental improvements. The goal is to fix missing or unclear information and enhance clarity, ensuring completeness. Key actions include: + • Audit and fill missing details – We’ll perform a thorough audit of each documentation page (guides and reference) to identify omissions. For example, ensure every request parameter and response field is documented (or at least linked to an explanation) in the API reference. If any endpoints are undocumented or lack descriptions, we will add them. One area to check is error handling – document common error responses and what they mean (e.g., 422 Unprocessable Entity for duplicate tracking requests, as shown in an example ). We will also confirm if rate limits or quotas exist and document those for transparency. This audit-driven approach closes the small gaps that might confuse users. + • Improve clarity and reduce jargon – As we revise content, we will rewrite any overly technical or jargon-heavy sections in plain language. For instance, the docs reference being JSONAPI-compliant and the tradeoffs of that ; we can simplify this by directly stating how the data is structured and reassure users that libraries can help, without diving too deep into jargon. We will also add a glossary for unavoidable terms (BOL, SCAC, LFD, etc.) or tooltips in the documentation site for quick definitions. This ensures that even readers without deep industry or technical knowledge can follow along. + • Reorganize subtly for flow – We’ll review the navigation structure to see if users find information easily. The current layout (Getting Started, In-Depth Guides, Useful Info, then endpoint reference) is logical. We plan only light reorganization if needed – for example, we might group all “integration basics” guides (webhooks, retries, test numbers) under a clear section. Another improvement could be to introduce an FAQ/Troubleshooting page (collecting common questions like why a tracking request might fail, how to handle delayed updates, etc., many of which are answered across different pages). Rather than a drastic restructure, these tweaks will make the docs more user-friendly without disrupting the overall familiar layout. + • Leverage user feedback – We will make use of the “Suggest edits” and “Raise issue” links in the docs . Any recurring issues or suggestions raised by the community will be incorporated. For example, if users often ask for clarification on a particular webhook event, we’ll update that documentation section promptly. Establishing a feedback loop will keep the documentation living and responsive to stakeholder needs. + • Iterate before overhaul – Only after implementing the above improvements and gathering feedback will we evaluate if a more aggressive restructure is necessary. Our intention is to avoid major upheaval unless the current format proves truly insufficient. If needed, we might consider a restructure such as merging similar guides, or creating a separate high-level overview section for non-developers. However, the priority is to first enhance content within the existing framework. This way, we deliver immediate value (clarity and completeness) to users and minimize confusion from a sudden docs reorganization. + +By systematically closing gaps and iterating, the documentation will become more polished and complete. Small details like surfacing the Test Numbers page (which is currently hidden under “Useful Info”) at relevant points can greatly improve the user experience  . Each improvement will prioritize making the integration process easier and more foolproof for all parties. + +Conclusion and Next Steps + +This strategic plan focuses on making Terminal49’s API documentation clear, complete, and welcoming for everyone – from software developers to operations managers. By enhancing endpoint examples, expanding educational content, improving onboarding with an event-driven mindset, and adding tutorials and role-specific guides, we address both the “how-to” and the “why it matters.” Throughout these updates, we’ll maintain an educational, jargon-free tone so that readers don’t feel intimidated and can quickly find the information they need. + +The improvements are designed to be actionable in phases. First, we will enrich content (examples, guides, clarifications) which yields an immediate boost in usability. Next, we’ll gather feedback and make iterative adjustments. Should deeper structural changes be warranted, we’ll approach them once we’ve stabilized the content itself. This measured approach ensures continuity: existing users find the docs increasingly helpful rather than suddenly unfamiliar, and new users get a smooth introduction. + +By executing this plan, Terminal49 will provide a documentation experience on par with its modern API – one that prioritizes clarity, completeness, and ease of integration. Ultimately, better documentation will reduce support requests, accelerate integrations, and instill confidence in all stakeholders that they can successfully leverage Terminal49’s platform for their logistics operations.   \ No newline at end of file diff --git a/docs/API-Documentation-Improvement-Tracker.md b/docs/API-Documentation-Improvement-Tracker.md new file mode 100644 index 00000000..8298f854 --- /dev/null +++ b/docs/API-Documentation-Improvement-Tracker.md @@ -0,0 +1,33 @@ +### Completed: +- Fix JSON code blocks in webhook guide ✅ +- Fix JSON code blocks in create-a-tracking-request.mdx ✅ +- Update authentication guide ✅ +- Develop error handling and troubleshooting documentation ✅ +- Update the navigation structure in docs.json ✅ +- Create a rate limiting guide explaining quotas and best practices ✅ +- Create a "Common Workflows" guide with end-to-end examples ✅ +- Create comprehensive API Quick Start guide ✅ +- Enhance JSON:API guide with practical examples ✅ +- Reorganize documentation structure by moving Getting Started content into guides ✅ + +### To Do: +- Develop pagination examples and best practices +- Add more language-specific code examples (Ruby, Go, etc.) +- Improve OpenAPI specification + +## Completed Updates + +- [x] Improved introduction and setup guidance in `docs/api-docs/getting-started/start-here.mdx` +- [x] Enhanced capabilities overview in `docs/api-docs/getting-started/capabilities-overview.mdx` +- [x] Created JSON:API guide in `docs/api-docs/in-depth-guides/json-api-guide.mdx` +- [x] Updated tracking request documentation in `docs/api-docs/api-reference/tracking-requests/create-a-tracking-request.mdx` +- [x] Created "Polling vs. Webhooks" comparison guide in `docs/api-docs/in-depth-guides/polling-vs-webhooks.mdx` +- [x] Created Webhook implementation guide in `docs/api-docs/in-depth-guides/webhooks.mdx` +- [x] Created Authentication guide in `docs/api-docs/in-depth-guides/authentication.mdx` +- [x] Fixed MDX parsing errors to ensure compatibility with Mintlify +- [x] Developed error handling documentation in `docs/api-docs/in-depth-guides/error-handling.mdx` +- [x] Created rate limiting guide in `docs/api-docs/in-depth-guides/rate-limiting.mdx` +- [x] Created Use Cases guide in `docs/api-docs/in-depth-guides/use-cases.mdx` +- [x] Created comprehensive Quickstart guide in `docs/api-docs/in-depth-guides/quickstart.mdx` +- [x] Enhanced JSON:API guide with practical examples +- [x] Reorganized documentation structure diff --git a/docs/Documentation-Navigation-Structure.md b/docs/Documentation-Navigation-Structure.md new file mode 100644 index 00000000..6b6a4d10 --- /dev/null +++ b/docs/Documentation-Navigation-Structure.md @@ -0,0 +1,161 @@ +# Terminal49 Documentation Navigation Structure + +This document outlines the proposed navigation structure for Terminal49's documentation portal. The documentation is organized into three main tabs with various sections within each tab. + +## Top-Level Navigation + +The documentation is divided into three main sections: + +| Tab | Purpose | +|-----|---------| +| **Integration Guide** | Focuses on use cases, quickstart guides, and conceptual information about integrating with Terminal49. | +| **API Reference** | Contains detailed technical documentation for specific API endpoints and operations. | +| **DataSync** | Covers Terminal49's data warehouse integration solution. | + +## Tab 1: Integration Guide + +This section helps users understand how to integrate Terminal49 into their systems and workflows. + +### Groups and Pages + +#### Group: [blank] +- home + +#### Group: Overview +- Quickstart (`api-docs/in-depth-guides/quickstart`) +- Terminal49 API Capabilities Overview (`api-docs/getting-started/capabilities-overview`) + +#### Group: Guides +- Use Cases (`api-docs/in-depth-guides/use-cases`) +- Polling vs. Webhooks (`api-docs/in-depth-guides/polling-vs-webhooks`) +- Webhooks (`api-docs/in-depth-guides/webhooks`) +- Tracking Shipments and Containers (`api-docs/getting-started/tracking-shipments-and-containers`) +- List Shipments and Containers (`api-docs/getting-started/list-shipments-and-containers`) +- Receive Status Updates (`api-docs/getting-started/receive-status-updates`) +- Adding Customer (`api-docs/in-depth-guides/adding-customer`) +- Tracking Request Lifecycle (`api-docs/in-depth-guides/tracking-request-lifecycle`) +- Event Timestamps (`api-docs/in-depth-guides/event-timestamps`) +- Terminal49 Map (`api-docs/in-depth-guides/terminal49-map`) +- Terminal49 Widget (`api-docs/in-depth-guides/terminal49-widget`) +- Rail Integration Guide (`api-docs/in-depth-guides/rail-integration-guide`) + +#### Group: Useful Info +- API Data Sources & Availability (`api-docs/useful-info/api-data-sources-availability`) +- Pricing (`api-docs/useful-info/pricing`) +- Test Numbers (`api-docs/useful-info/test-numbers`) +- Tracking Request Retrying (`api-docs/useful-info/tracking-request-retrying`) +- Webhook Events Examples (`api-docs/useful-info/webhook-events-examples`) + +## Tab 2: API Reference + +This section provides detailed technical documentation for specific API endpoints. + +### Groups and Pages + +#### Group: API Concepts +- Authentication (`api-docs/in-depth-guides/authentication`) +- Error Handling (`api-docs/in-depth-guides/error-handling`) +- Rate Limiting (`api-docs/in-depth-guides/rate-limiting`) +- JSON:API Guide (`api-docs/in-depth-guides/json-api-guide`) +- Including Resources (`api-docs/in-depth-guides/including-resources`) + +#### Group: Shipments +- List Shipments (`api-docs/api-reference/shipments/list-shipments`) +- Get a Shipment (`api-docs/api-reference/shipments/get-a-shipment`) +- Edit a Shipment (`api-docs/api-reference/shipments/edit-a-shipment`) +- Stop Tracking Shipment (`api-docs/api-reference/shipments/stop-tracking-shipment`) +- Resume Tracking Shipment (`api-docs/api-reference/shipments/resume-tracking-shipment`) + +#### Group: Tracking Requests +- List Tracking Requests (`api-docs/api-reference/tracking-requests/list-tracking-requests`) +- Create a Tracking Request (`api-docs/api-reference/tracking-requests/create-a-tracking-request`) +- Get a Single Tracking Request (`api-docs/api-reference/tracking-requests/get-a-single-tracking-request`) +- Edit a Tracking Request (`api-docs/api-reference/tracking-requests/edit-a-tracking-request`) + +#### Group: Webhooks +- Get Single Webhook (`api-docs/api-reference/webhooks/get-single-webhook`) +- Delete a Webhook (`api-docs/api-reference/webhooks/delete-a-webhook`) +- Edit a Webhook (`api-docs/api-reference/webhooks/edit-a-webhook`) +- List Webhooks (`api-docs/api-reference/webhooks/list-webhooks`) +- Create a Webhook (`api-docs/api-reference/webhooks/create-a-webhook`) +- List Webhook IPs (`api-docs/api-reference/webhooks/list-webhook-ips`) + +#### Group: Webhook Notifications +- Get a Single Webhook Notification (`api-docs/api-reference/webhook-notifications/get-a-single-webhook-notification`) +- List Webhook Notifications (`api-docs/api-reference/webhook-notifications/list-webhook-notifications`) +- Get Webhook Notification Payload Examples (`api-docs/api-reference/webhook-notifications/get-webhook-notification-payload-examples`) + +#### Group: Containers +- List Containers (`api-docs/api-reference/containers/list-containers`) +- Edit a Container (`api-docs/api-reference/containers/edit-a-container`) +- Get a Container (`api-docs/api-reference/containers/get-a-container`) +- Get a Container's Raw Events (`api-docs/api-reference/containers/get-a-containers-raw-events`) +- Get a Container's Transport Events (`api-docs/api-reference/containers/get-a-containers-transport-events`) + +#### Group: Shipping Lines +- Shipping Lines (`api-docs/api-reference/shipping-lines/shipping-lines`) +- Get a Single Shipping Line (`api-docs/api-reference/shipping-lines/get-a-single-shipping-line`) + +#### Group: Metro Areas +- Get a Metro Area Using the UNLOCODE or the ID (`api-docs/api-reference/metro-areas/get-a-metro-area-using-the-unlocode-or-the-id`) + +#### Group: Ports +- Get a Port Using the LOCODE or the ID (`api-docs/api-reference/ports/get-a-port-using-the-locode-or-the-id`) + +#### Group: Vessels +- Get a Vessel Using the ID (`api-docs/api-reference/vessels/get-a-vessel-using-the-id`) +- Get a Vessel Using the IMO (`api-docs/api-reference/vessels/get-a-vessel-using-the-imo`) + +#### Group: Terminals +- Get a Terminal Using the ID (`api-docs/api-reference/terminals/get-a-terminal-using-the-id`) + +#### Group: Parties +- List Parties (`api-docs/api-reference/parties/list-parties`) +- Create a Party (`api-docs/api-reference/parties/create-a-party`) +- Get a Party (`api-docs/api-reference/parties/get-a-party`) +- Edit a Party (`api-docs/api-reference/parties/edit-a-party`) + +## Tab 3: DataSync + +This section covers Terminal49's data warehouse integration solution. + +### Groups and Pages + +#### Group: [blank] +- home (`datasync/home`) + +#### Group: [blank] +- Overview (`datasync/overview`) +- Supported Destinations (`datasync/supported-destinations`) + +#### Group: Table Properties +- Containers Rail (`datasync/table-properties/containers_rail`) +- Shipments (`datasync/table-properties/shipments`) +- Tracking Requests (`datasync/table-properties/tracking-requests`) +- Transport Events (`datasync/table-properties/transport-events`) +- Transfer Status (`datasync/table-properties/transfer-status`) +- Containers (`datasync/table-properties/containers`) + +## Notes on Structure + +1. **Integration Guide to API Reference Flow**: + - The Integration Guide introduces high-level concepts, use cases, and integration patterns. + - The API Reference contains technical implementation details including API concepts and endpoints. + - This creates a natural learning progression from business problems to technical solutions. + +2. **Use Case Focus**: + - The Use Cases document in the Integration Guide helps users understand real-world applications. + - API concepts in the API Reference tab support technical implementation of these use cases. + +3. **Clear Separation of Concerns**: + - Integration Guide: Focuses on "why" and "what" (business problems, use cases, general patterns) + - API Reference: Focuses on "how" (technical implementations, API details, specifications) + - DataSync: Dedicated to the data warehouse integration solution + +4. **Naming Conventions**: + - Groups use clear, descriptive names. + - Page titles are consistently formatted. + +5. **Potential Improvements**: + - Consider organizing the Guides section in the Integration Guide into subcategories as it grows. + - Add cross-references between use cases in the Integration Guide and corresponding API endpoints in the Reference section. diff --git a/docs/api-docs/api-reference/tracking-requests/create-a-tracking-request.mdx b/docs/api-docs/api-reference/tracking-requests/create-a-tracking-request.mdx index 7b804f03..c86b72af 100644 --- a/docs/api-docs/api-reference/tracking-requests/create-a-tracking-request.mdx +++ b/docs/api-docs/api-reference/tracking-requests/create-a-tracking-request.mdx @@ -1,5 +1,420 @@ --- +title: Create a Tracking Request +description: Submit a new tracking request to monitor ocean shipments via bill of lading, booking number, or container number. og:title: Create a Tracking Request | Terminal49 API Documentation -og:description: Create tracking requests via Terminal49's API for accurate shipment tracking and notifications. +og:description: Create tracking requests via Terminal49's API for accurate shipment tracking and real-time notifications. openapi: post /tracking_requests ---- \ No newline at end of file +--- + +## Endpoint Purpose +Submitting a tracking request tells Terminal49 to start monitoring a specific shipment for you. This is typically the first step in the tracking workflow. + +## Business Use Cases +- **Initial Tracking Setup**: Start tracking a new container shipment when you receive a bill of lading or booking confirmation. +- **Customer Onboarding**: Quickly set up tracking for a customer's shipments when they provide their shipping documents. +- **Bulk Import**: Programmatically submit multiple tracking requests to monitor your entire inventory in transit. + +## Example Scenarios + + + + ```json title="Request" + { + "data": { + "type": "tracking_request", + "attributes": { + "request_type": "bill_of_lading", + "request_number": "MAEU9736478", + "scac": "MAEU" + } + } + } + ``` + + ```json title="Response" + { + "data": { + "id": "b9c6a3e0-411e-4378-b93c-aacea42a673e", + "type": "tracking_request", + "attributes": { + "request_type": "bill_of_lading", + "request_number": "MAEU9736478", + "scac": "MAEU", + "status": "pending", + "failed_reason": null, + "created_at": "2023-05-15T14:23:12Z", + "updated_at": "2023-05-15T14:23:12Z" + }, + "relationships": { + "shipment": { + "data": null + } + } + } + } + ``` + + **Notes:** + - The bill of lading is typically the most reliable identifier for tracking shipments. + - The `status` will initially be `pending` while Terminal49 processes your request. + - Once processing completes, you'll receive a webhook notification and the status will change to `succeeded` or `failed`. + + + + ```json title="Request" + { + "data": { + "type": "tracking_request", + "attributes": { + "request_type": "container", + "request_number": "MSCU1234567", + "scac": "MSCU" + } + } + } + ``` + + ```json title="Response" + { + "data": { + "id": "f7c9b5d2-311e-4378-a91c-bbcea42a785f", + "type": "tracking_request", + "attributes": { + "request_type": "container", + "request_number": "MSCU1234567", + "scac": "MSCU", + "status": "pending", + "failed_reason": null, + "created_at": "2023-05-15T15:45:22Z", + "updated_at": "2023-05-15T15:45:22Z" + }, + "relationships": { + "shipment": { + "data": null + } + } + } + } + ``` + + **Notes:** + - Container number tracking support varies between carriers. Refer to the Carrier Data Matrix to verify support. + - Container numbers should include the carrier prefix (e.g., MSCU for MSC containers). + - Container tracking can sometimes fail if the container is very new or not yet associated with a booking in the carrier's system. + + + + ```json title="Request" + { + "data": { + "type": "tracking_request", + "attributes": { + "request_type": "bill_of_lading", + "request_number": "TEST-TR-SUCCEEDED", + "scac": "TEST" + } + } + } + ``` + + ```json title="Response" + { + "data": { + "id": "e8d7c6b5-211d-4378-a81b-aacfa42a895e", + "type": "tracking_request", + "attributes": { + "request_type": "bill_of_lading", + "request_number": "TEST-TR-SUCCEEDED", + "scac": "TEST", + "status": "pending", + "failed_reason": null, + "created_at": "2023-05-15T16:30:45Z", + "updated_at": "2023-05-15T16:30:45Z" + }, + "relationships": { + "shipment": { + "data": null + } + } + } + } + ``` + + **Notes:** + - Using test numbers is perfect for development and testing your integration. + - `TEST-TR-SUCCEEDED` will always trigger a successful tracking response. + - `TEST-TR-FAILED` will always trigger a failed tracking response. + - These test numbers will trigger corresponding webhook events for complete testing. + + + + ```json title="Request" + { + "data": { + "type": "tracking_request", + "attributes": { + "request_type": "bill_of_lading", + "request_number": "INVALID-FORMAT", + "scac": "UNKNOWN" + } + } + } + ``` + + ```json title="Response" + { + "errors": [ + { + "status": "422", + "title": "Invalid SCAC", + "detail": "The SCAC 'UNKNOWN' is not supported.", + "source": { + "pointer": "/data/attributes/scac" + } + } + ] + } + ``` + + **Notes:** + - This example shows a validation error for an invalid SCAC code. + - Common errors include invalid SCAC codes, improperly formatted request numbers, or duplicate tracking requests. + - For a complete list of supported SCACs, refer to the Carrier Data Matrix. + + + +## Key Response Fields + + + Unique identifier for the tracking request. Store this to check status later. + + + + Current status of the tracking request. Possible values: + - `pending`: Request is being processed + - `succeeded`: Container/shipment located and tracking established + - `failed`: Unable to track the shipment (see failed_reason) + - `awaiting_manifest`: Bill of lading is valid but not yet available in the carrier's system + - `tracking_stopped`: Tracking has been stopped for this request + + + + If status is "failed", this field provides the reason. Common values: + - `duplicate`: Shipment already exists in Terminal49 + - `not_found`: Shipping line couldn't find the tracking number + - `retries_exhausted`: Maximum retries reached without success + - `invalid_number`: Improperly formatted tracking number + + + See the [Tracking Request Lifecycle](/api-docs/in-depth-guides/tracking-request-lifecycle) guide for a complete list of failure reasons. + + + + + When the tracking request succeeds, this contains a reference to the created shipment. + + + + ID of the linked shipment that you can use to retrieve full shipment details. + + + + Always "shipment" for successful tracking requests. + + + + +## Related Webhook Events + +When creating a tracking request, you may receive the following webhook events: + + + Sent when Terminal49 successfully locates your shipment and begins tracking it. The notification includes details about the created shipment and its containers. + + + + Sent when Terminal49 is unable to locate your shipment after all retry attempts. Includes the failed_reason to help troubleshoot. + + + + Sent when the bill of lading number is valid but the shipment data is not yet available in the carrier's system. Terminal49 will continue checking daily. + + +## Related Guides & Tutorials + + + + Learn about the full lifecycle of a tracking request, including all possible statuses and failure reasons. + + + Learn how to set up webhooks to receive real-time notifications about your tracking requests. + + + Complete list of test tracking numbers you can use during development. + + + Learn how to efficiently submit and manage multiple tracking requests. + + + +## Troubleshooting + + + + **Problem**: Your tracking request has been in "pending" status for an extended period. + + **Solution**: + 1. Terminal49 retries tracking requests up to 14 times over 24 hours. + 2. Check that the SCAC and request number are correct. + 3. For bill of lading numbers, note that they may remain in "awaiting_manifest" status for up to 7 days. + 4. If the issue persists beyond 24 hours, contact support@terminal49.com. + + + + **Problem**: Your tracking request failed with reason "not_found". + + **Solution**: + 1. Verify the tracking number format is correct (no spaces, proper prefixes). + 2. Confirm you're using the correct SCAC code for the carrier. + 3. For newly issued bills of lading, try again in 24-48 hours as it may take time to appear in the carrier's system. + 4. If using a container number, check if the carrier supports container tracking in the Carrier Data Matrix. + + + + **Problem**: Your tracking request failed with reason "duplicate". + + **Solution**: + 1. The shipment is already being tracked in Terminal49. + 2. Use `GET /shipments` with a filter to find the existing shipment: + + ```bash + GET /shipments?filter[bill_of_lading_number]=MAEU9736478 + ``` + + 3. You can access the existing shipment data instead of creating a new tracking request. + + + + + + **Scenario**: You want to create a tracking request using a booking number. + + **Example**: + ```json + { + "data": { + "attributes": { + "request_type": "booking_number", + "request_number": "OOLU8324567", + "scac": "OOLU" + }, + "type": "tracking_request" + } + } + ``` + + **Notes**: + - Booking numbers are typically issued by the carrier early in the shipping process + - Using booking numbers allows you to start tracking earlier in the shipping lifecycle + - Booking number format varies by carrier (e.g., OOLU prefix for ONE Line bookings) + + + + **Scenario**: You want to add your own reference numbers to help identify the shipment. + + **Example**: + ```json + { + "data": { + "attributes": { + "request_type": "bill_of_lading", + "request_number": "MEDUFR030802", + "scac": "MSCU", + "ref_numbers": [ + "PO12345", + "HBL12345", + "INV9876" + ] + }, + "type": "tracking_request" + } + } + ``` + + **Notes**: + - Reference numbers can include purchase orders, house bills of lading, invoice numbers, etc. + - You can add multiple reference numbers to a single tracking request + - These numbers will be searchable later through the API and UI + - You can use reference numbers to link shipments to your internal systems + + + + **Scenario**: You want to categorize your shipments with tags for easier filtering. + + **Example**: + ```json + { + "data": { + "attributes": { + "request_type": "bill_of_lading", + "request_number": "MAEU9736478", + "scac": "MAEU", + "shipment_tags": [ + "priority", + "electronics", + "q2-2023" + ] + }, + "type": "tracking_request" + } + } + ``` + + **Notes**: + - Tags provide a flexible way to group and categorize shipments + - Common uses include priority levels, product categories, or time periods + - Tags can be used for filtering when listing shipments + - You can add or remove tags later using the Edit Shipment endpoint + + + + **Scenario**: You're tracking shipments on behalf of your customers and want to associate shipments with specific customers. + + **Example**: + ```json + { + "data": { + "attributes": { + "request_type": "bill_of_lading", + "request_number": "MAEU9736478", + "scac": "MAEU" + }, + "relationships": { + "customer": { + "data": { + "id": "f7cb530a-9e60-412c-a5bc-205a2f34ba54", + "type": "party" + } + } + }, + "type": "tracking_request" + } + } + ``` + + **Notes**: + - The customer must first be created using the Parties API + - The customer ID is used to link the shipment to the specific customer + - This association enables filtering shipments by customer + - Customer information will be included in webhook notifications when using appropriate includes + - See the [Adding Customer guide](/api-docs/in-depth-guides/adding-customer) for more details + + + + + For the fastest tracking setup, use the master bill of lading (MBL) number from the carrier. House bills of lading (HBL) are not supported. + + + + Container number tracking is not supported by all carriers. Always check the Carrier Data Matrix before implementing container tracking. + diff --git a/docs/api-docs/data-sources/overview.mdx b/docs/api-docs/data-sources/overview.mdx new file mode 100644 index 00000000..0a5ebbf1 --- /dev/null +++ b/docs/api-docs/data-sources/overview.mdx @@ -0,0 +1,85 @@ +--- +title: Data Sources Overview +description: Learn about Terminal49's data sources, data quality, and availability for comprehensive container tracking. +icon: "database" +--- + +# Terminal49 Data Sources + +Terminal49 aggregates data from multiple sources to provide comprehensive visibility into container shipments. This overview introduces our data sources and helps you understand data availability and quality considerations. + +## Our Data Ecosystem + + + + Data from major steamship lines including bills of lading, bookings, vessel ETAs, containers, and milestones. + + + Container availability, last free day, holds, fees, and other terminal-specific information. + + + Container milestones for rail transport including rail loading, departure, arrival, and unloading events. + + + Vessel details and real-time location tracking through Automatic Identification System data. + + + +## Data Integration Approach + +Terminal49 takes raw data from various sources and transforms it into a standardized format to provide a consistent experience across carriers and terminals. Our approach includes: + +1. **Normalization** - Converting different formats and terms into a consistent schema +2. **Enrichment** - Adding context and relationships between data points +3. **Validation** - Checking for errors and inconsistencies +4. **Standardization** - Using common formats for dates, locations, and status information + +## Coverage and Limitations + +While Terminal49 strives to provide comprehensive data, availability varies by source: + + + We support all major ocean carriers including Maersk, MSC, CMA CGM, COSCO, Hapag-Lloyd, ONE, Evergreen, and many more. Each carrier provides different data points with varying quality and timeliness. + + [View detailed carrier coverage](/api-docs/useful-info/api-data-sources-availability#supported-ocean-carriers) + + + + Terminal49 integrates with terminals at major ports across North America, Europe, and Asia. Terminal data quality varies significantly by location. + + [View detailed terminal coverage](/api-docs/useful-info/api-data-sources-availability#ports-and-terminals) + + + + We support all Class 1 North American railroads, including BNSF, CN, CP, CSX, NS, and UP. Rail data is standardized across carriers but certain event types may only be available from specific rail providers. + + [View detailed rail coverage](/api-docs/useful-info/api-data-sources-availability#rail-carriers) + + +## Data Quality Considerations + +When building applications with Terminal49 data, consider these data quality factors: + +- **Timeliness** - Data latency varies by source; carrier updates might take several hours while terminal data is typically more real-time +- **Completeness** - Not all data fields are available from all sources (see [Known Issues](/api-docs/useful-info/api-data-sources-availability#known-issues-ocean)) +- **Accuracy** - ETAs and availability predictions are based on carrier and terminal inputs and can change +- **Consistency** - Terminal49 normalizes data, but underlying source inconsistencies may occasionally surface + +## Getting Started with Terminal49 Data + + + + Review the [detailed data sources documentation](/api-docs/useful-info/api-data-sources-availability) to understand which data points are consistently available. + + + Design your integration to handle missing or delayed data from certain sources. + + + Leverage our standardized data formats rather than trying to parse raw carrier or terminal data. + + + Use webhooks to receive real-time updates when data changes rather than frequent polling. + + + +For a detailed breakdown of supported data fields and their availability, see our [API Data Sources and Availability](/api-docs/useful-info/api-data-sources-availability) documentation. diff --git a/docs/api-docs/embedding/overview.mdx b/docs/api-docs/embedding/overview.mdx new file mode 100644 index 00000000..c64ceb21 --- /dev/null +++ b/docs/api-docs/embedding/overview.mdx @@ -0,0 +1,68 @@ +--- +title: Embedding Overview +description: Learn how to embed Terminal49's tracking and visualization tools directly into your website to provide real-time container tracking to your customers. +icon: "code" +--- + +# Terminal49 Embedding Solutions + +Terminal49 offers powerful embedding solutions that allow you to integrate real-time container tracking directly into your website. These tools enhance your customer experience by providing up-to-date shipping information without requiring users to visit another platform. + +## Available Embedding Options + + + + A dynamic, visual map showing container journeys in real-time. Perfect for providing a visual representation of shipment progress. + + [Learn More](/api-docs/in-depth-guides/terminal49-map) + + + A customizable tracking widget that allows your customers to look up their shipments directly on your website. + + [Learn More](/api-docs/in-depth-guides/terminal49-widget) + + + +## How to Choose the Right Solution + +| Feature | Embeddable Map | Track & Trace Widget | +|---------|---------------|----------------------| +| **Use Case** | Visualize container journeys with detailed map-based tracking | Enable users to search and track their shipments | +| **Integration** | Requires JavaScript integration | Simple embed with just a few lines of code | +| **Customization** | Highly customizable with API | Basic customization options | +| **Container Data** | Focused on geographic visualization | Focused on status and milestone information | +| **User Interaction** | Interactive map with timeline and events | Search functionality with detailed results | + +## Getting Started + +To start using either embedding solution, you'll need: + +1. A Terminal49 account +2. A publishable API key (contact support@terminal49.com) +3. Basic knowledge of HTML and JavaScript + +Each embedding solution has its own comprehensive documentation: + +- [Embeddable Map Guide](/api-docs/in-depth-guides/terminal49-map) +- [Track & Trace Widget Guide](/api-docs/in-depth-guides/terminal49-widget) + +## Common Use Cases + + + + Enhance your customer portal with visual tracking to differentiate your service offering. + + + Let customers track the progress of their international shipments directly on your site. + + + Provide clients with real-time visibility into their container movements. + + + Integrate tracking data into your internal systems for better visibility. + + + +## Pricing + +Both embedding solutions require a subscription. Contact [sales@terminal49.com](mailto:sales@terminal49.com) for current pricing information and to discuss which solution best fits your needs. diff --git a/docs/api-docs/getting-started/capabilities-overview.mdx b/docs/api-docs/getting-started/capabilities-overview.mdx new file mode 100644 index 00000000..a439e077 --- /dev/null +++ b/docs/api-docs/getting-started/capabilities-overview.mdx @@ -0,0 +1,181 @@ +--- +title: Terminal49 API Capabilities Overview +description: A comprehensive overview of all features and capabilities available through the Terminal49 API. +--- + +# Terminal49 API Capabilities + +Terminal49's API provides end-to-end ocean freight visibility through a collection of endpoints that deliver real-time tracking data, reference information, and event notifications. This overview will help you understand what's possible and how each feature fits into your logistics workflows. + +## Core Capabilities + + + + Track containers and shipments across their entire journey using bill of lading, booking, or container numbers. + + + Receive real-time updates via webhooks when shipment status changes, from origin to destination. + + + Access standardized data about carriers, ports, terminals, and more for consistent reporting. + + + Get comprehensive container information including status, location, free time, and holds. + + + +## End-to-End Visibility + +Terminal49 provides visibility across the entire container journey: + + + + Track empty container pickup, loading, and departure from origin port. + + + Follow vessel movements and receive ETA updates throughout the journey. + + + Get notified when the vessel arrives, berths, and when containers are discharged. + + + Monitor container availability, customs status, holds, and last free day information. + + + Track rail departures, arrivals, and final delivery events to inland destinations. + + + +## Key Differentiators + + + + Terminal49 standardizes data from different carriers into a single consistent format. For example, each carrier might report a container discharge event differently, but Terminal49 normalizes these into the same event type (`container.transport.vessel_discharged`) with standardized timestamps and location formats. + + **Business Benefit**: Build once, track across all carriers without custom integration for each shipping line. + + + + Instead of constantly polling for updates, Terminal49 notifies you via webhooks when meaningful events occur. This includes status changes, ETA updates, terminal holds, and changes to last free day. + + **Business Benefit**: Respond immediately to delays, holds, or availability changes, reducing demurrage costs and improving planning. + + + + Terminal49 actively monitors and notifies you of changes to Last Free Day (LFD) - the date by which containers must be picked up to avoid storage fees. This data is often buried in terminal websites and subject to change. + + **Business Benefit**: Avoid costly demurrage fees by prioritizing pickups based on accurate LFD information. + + + + The API provides comprehensive information about holds (customs, freight, terminal) and other exceptions that may delay pickup. + + **Business Benefit**: Proactively address holds and plan alternative shipments when delays occur. + + + +## How It Works + + + ![Terminal49 API Flow Diagram](/images/api-flow-diagram.png) + + +1. **Submit tracking requests** using bill of lading, booking, or container numbers +2. **Terminal49 monitors** these shipments by aggregating data from multiple sources +3. **Your systems receive updates** through webhooks when any change occurs +4. **Query additional information** as needed using the REST API endpoints + +## Available Data Points + +Here are key data points available through the Terminal49 API: + + + + - Bill of lading numbers + - Booking numbers + - Port of loading/discharge + - Shipper/consignee information + - Vessel assignments + - Estimated dates (departure, arrival) + - Associated containers + + + + - Container numbers and size/type + - Current location and status + - Last free day information + - Terminal availability + - Customs/terminal/freight holds + - Transport events history + - Pickup appointments + + + + - Vessel departures and arrivals + - Container loading/discharge + - Customs and terminal holds + - Availability changes + - Last free day updates + - Rail movements + - Terminal cutoffs + + + + - Shipping lines (with SCAC codes) + - Terminals and ports + - Vessels and voyages + - Metro areas + - Standardized location formats + + + +## Common Use Cases + + + + Connect Terminal49 to your Transportation Management System for real-time visibility without manual tracking. + + + + Build automated alerts to notify customers about shipment status changes, delays, or container availability. + + + + Prioritize container pickups based on last free day information to avoid demurrage charges. + + + + Identify and resolve customs holds, freight issues, or delays that require intervention. + + + + Build real-time dashboards showing shipment status across your supply chain. + + + + Plan warehouse staffing and inventory based on accurate container arrival predictions. + + + +## Getting Started + +Ready to start integrating with Terminal49? Here's how to begin: + + + + [Sign up for Terminal49](https://app.terminal49.com/signup) and get your API key from the [developer portal](https://app.terminal49.com/developers/api-keys). + + + Follow our [quick start guide](/api-docs/getting-started/tracking-shipments-and-containers) to submit your first tracking request. + + + Configure [webhooks](/api-docs/in-depth-guides/webhooks) to receive real-time updates about your shipments. + + + Browse our [API reference](/api-docs/api-reference) to discover all available endpoints. + + + + + Our team is here to help you get the most out of Terminal49. Contact us at [support@terminal49.com](mailto:support@terminal49.com) with any questions about our API capabilities. + diff --git a/docs/api-docs/getting-started/start-here.mdx b/docs/api-docs/getting-started/start-here.mdx index 5c63bf6f..f93df26d 100644 --- a/docs/api-docs/getting-started/start-here.mdx +++ b/docs/api-docs/getting-started/start-here.mdx @@ -1,23 +1,99 @@ --- title: 1. Start Here +description: Begin your journey with Terminal49's API for comprehensive ocean freight tracking. --- -So you want to start tracking your ocean shipments and containers and you have a few BL numbers. Follow the guide. -Our API responses use [JSONAPI](https://jsonapi.org/) schema. There are [client libraries](https://jsonapi.org/implementations/#client-libraries) available in almost every language. Our API should work with these libs out of the box. +# Getting Started with Terminal49 API -Our APIs can be used with any HTTP client; choose your favorite! We love Postman, it's a friendly graphical interface to a powerful cross-platform HTTP client. Best of all it has support for the OpenAPI specs that we publish with all our APIs. We have created a collection of requests for you to easily test the API endpoints with your API Key. Link to the collection below. +Terminal49's API gives you programmatic access to real-time ocean freight tracking data across multiple carriers. With our API, you can monitor containers from origin to destination, receive event notifications, access terminal availability information, and much more. - -**Run in Postman** +## What Can You Do With Terminal49's API? + + + + Monitor shipments using bill of lading, booking, or container numbers across major ocean carriers. + + + Get notified via webhooks when containers arrive, encounter holds, or change status. + + + Retrieve comprehensive container information including status, location, and last free day. + + + Explore all features and use cases of Terminal49's API. + + + + + New to Terminal49? Check out our [Capabilities Overview](/api-docs/getting-started/capabilities-overview) for a comprehensive look at what our API can do for your logistics operations. + + +## API Basics + +Our API follows the [JSON:API](https://jsonapi.org/) specification, providing a standardized way to request and manipulate resources. This makes our API predictable and easier to work with. + + + All Terminal49 API responses follow the JSON:API structure: + + ```json + { + "data": { + "id": "123", + "type": "shipment", + "attributes": { + "bill_of_lading_number": "MAEU9736478", + // Other shipment attributes + }, + "relationships": { + "containers": { + "data": [ + { "id": "456", "type": "container" } + ] + } + } + } + } + ``` + + For more details, see our [JSON:API Guide](/api-docs/in-depth-guides/json-api-guide). + + +There are [client libraries](https://jsonapi.org/implementations/#client-libraries) available in almost every language. Our API should work with these libraries out of the box, making integration easier. + +## Tools to Get Started + +Our APIs can be used with any HTTP client. We've created a Postman collection so you can quickly test endpoints with your API key: + + + **Run in Postman** -*** ## Get an API Key + Sign in to your Terminal49 account and go to your [developer portal](https://app.terminal49.com/developers/api-keys) page to get your API key. ### Authentication -When passing your API key it should be prefixed with `Token`. For example, if your API Key is 'ABC123' then your Authorization header would look like: + +When passing your API key, it should be prefixed with `Token`. For example, if your API Key is 'ABC123' then your Authorization header would look like: ``` "Authorization": "Token ABC123" -``` \ No newline at end of file +``` + +## Next Steps + + + + Submit tracking requests for your [shipments and containers](/api-docs/getting-started/tracking-shipments-and-containers). + + + Configure [webhooks](/api-docs/in-depth-guides/webhooks) to receive real-time updates. + + + Browse our [API reference](/api-docs/api-reference) for all available endpoints. + + + + + For the fastest start, check out our [Quickstart Guide](/api-docs/in-depth-guides/quickstart) which will get you up and running with minimal effort. + diff --git a/docs/api-docs/getting-started/tracking.mdx b/docs/api-docs/getting-started/tracking.mdx new file mode 100644 index 00000000..1f2792a6 --- /dev/null +++ b/docs/api-docs/getting-started/tracking.mdx @@ -0,0 +1,564 @@ +--- +title: Tracking Guide +description: Learn about all methods of tracking shipments and containers in Terminal49, the tracking request lifecycle, and available API endpoints. +icon: "container-storage" +--- + +Tracking is a core function of Terminal49's platform. This guide covers all aspects of tracking shipments and containers, from initiating tracking through various methods to understanding the tracking request lifecycle and managing tracking data. + + +## Tracking Methods + +Terminal49 offers multiple ways to start tracking your shipments or containers: + + + + Use the Tracking Request API to programmatically submit container numbers, bills of lading, or booking numbers for tracking. + + + Manually enter tracking numbers or upload your spreadheet/CSV through Terminal49's web dashboard for immediate tracking. + + + Email CSV files with multiple tracking references to quickly set up tracking for multiple shipments at once. + + + +### API Integration + +The most flexible way to integrate tracking into your systems is via the Terminal49 API. Submit tracking requests programmatically using the Tracking Request endpoint: + +```bash +POST /v2/tracking_requests +``` + +This method is ideal for: +- Real-time tracking initiation from your TMS or other internal systems +- Automated tracking workflows +- High-volume tracking needs +- Custom reference data and shipment organization + +[Detailed API Reference →](/api-docs/api-reference/tracking-requests/create-a-tracking-request) + +## Tracking Number Support + +Terminal49 supports tracking using three primary reference types: + + + +

The master bill of lading is issued by the ocean carrier and serves as the contract of carriage.

+ + **Format Examples:** + - Maersk: MAEU9736478 + - MSC: MSDUV4812756 + - CMA CGM: CMDUV0946721 + + **Best Practices:** + - MBL is typically the most reliable reference for tracking + - Include the carrier's SCAC code when submitting + - Some carriers may not make shipment data available immediately after issuing the MBL + + **Coverage:** + - Supported by all major ocean carriers + - Provides visibility from origin to destination + - Typically covers all containers in a shipment +
+ + +

A booking number is assigned by the carrier when space is reserved on a vessel.

+ + **Format Examples:** + - Maersk: MAEU785421 + - ONE: OOLU3456723 + - Hapag-Lloyd: HLCUEUR6703426 + + **Best Practices:** + - Use booking numbers to begin tracking earlier in the shipping lifecycle + - Some carriers include their SCAC code at the beginning of booking numbers + - Booking numbers may be replaced by bills of lading once issued + + **Coverage:** + - Available earlier than bills of lading + - May provide limited information until container assignment + - Supported by most major carriers (see Carrier Data Matrix for details) +
+ + +

A container number uniquely identifies a specific shipping container.

+ + **Format Examples:** + - MSCU1234567 + - CMAU7654321 + - OOLU8765432 + + **Best Practices:** + - Container numbers follow ISO 6346 format (4 letters + 7 numbers) + - Always include the carrier prefix (first 4 characters) + - For multi-container shipments, you'll need to track each container separately + + **Coverage:** + - Container level detail provides the most granular tracking + - Not supported by all carriers (check Carrier Data Matrix) + - May not be available until containers are assigned to a booking +
+
+ + + House Bill of Lading (HBL) numbers issued by freight forwarders are not directly supported for tracking. You must use the Master Bill of Lading (MBL) from the ocean carrier. + + +### Dashboard Interface + +For manual tracking or lower volumes, Terminal49's web dashboard offers a user-friendly interface: + +1. Log in to your Terminal49 account +2. Navigate to the "Track" section +3. Enter your tracking number(s) and select the carrier +4. Click "Track" to begin monitoring + +This method is ideal for: +- Quick one-off tracking needs +- Small shipment volumes +- Testing or verification purposes +- Users who prefer a visual interface +### Bulk CSV Upload via Email + +For setting up tracking for multiple shipments at once: + +1. Prepare a CSV/spreadsheet file with your tracking details +2. Email the file to track@terminal49.com +3. Terminal49 will process the file and begin tracking all shipments + +For detailed instructions on CSV formatting options and requirements, see our [help documentation](https://help.terminal49.com/articles/4091681959-how-to-track-shipments-or-containers-via-email). + +This method is ideal for: +- Initial onboarding of many shipments +- Regular batch imports from other systems +- Scheduled tracking updates +- Teams that prefer working with spreadsheets + + + + +## Tracking Request API Details + +When using the API to create tracking requests, you can provide a wealth of additional information to enhance your tracking experience: + +### Basic Required Parameters + +```json +{ + "data": { + "type": "tracking_request", + "attributes": { + "request_type": "bill_of_lading", + "request_number": "MAEU9736478", + "scac": "MAEU" + } + } +} +``` + +### Optional Organizational Parameters + + + An array of your own reference numbers like purchase orders, SKUs, or internal IDs that help you identify this shipment in your systems + + + + Tags for categorizing shipments (e.g., "priority", "fragile", "q2-2023") that can be used for filtering + + +### Relationship Parameters + +You can associate the shipment with a customer by establishing a relationship: + +```json +{ + "data": { + "type": "tracking_request", + "attributes": { + "request_type": "bill_of_lading", + "request_number": "MAEU9736478", + "scac": "MAEU" + }, + "relationships": { + "customer": { + "data": { + "id": "a9e52f3d-2fa9-467c-8deb-09e90dac2f0b", + "type": "party" + } + } + } + } +} +``` + + + The customer must be created first using the Parties API before it can be referenced in a tracking request. + + +### Example Scenarios + + + + ```json + { + "data": { + "attributes": { + "request_type": "booking", + "request_number": "OOLU8324567", + "scac": "OOLU" + }, + "type": "tracking_request" + } + } + ``` + + **Notes**: + - Booking numbers are typically issued by the carrier early in the shipping process + - Using booking numbers allows you to start tracking earlier in the shipping lifecycle + - Booking number format varies by carrier (e.g., OOLU prefix for ONE Line bookings) + + + + ```json + { + "data": { + "attributes": { + "request_type": "bill_of_lading", + "request_number": "MEDUFR030802", + "scac": "MSCU", + "ref_numbers": [ + "PO12345", + "HBL12345", + "INV9876" + ] + }, + "type": "tracking_request" + } + } + ``` + + **Notes**: + - Reference numbers can include purchase orders, house bills of lading, invoice numbers, etc. + - You can add multiple reference numbers to a single tracking request + - These numbers will be searchable later through the API and UI + - You can use reference numbers to link shipments to your internal systems + + + + ```json + { + "data": { + "attributes": { + "request_type": "bill_of_lading", + "request_number": "MAEU9736478", + "scac": "MAEU", + "shipment_tags": [ + "priority", + "electronics", + "q2-2023" + ] + }, + "type": "tracking_request" + } + } + ``` + + **Notes**: + - Tags provide a flexible way to group and categorize shipments + - Common uses include priority levels, product categories, or time periods + - Tags can be used for filtering when listing shipments + - You can add or remove tags later using the Edit Shipment endpoint + + + + ```json + { + "data": { + "attributes": { + "request_type": "bill_of_lading", + "request_number": "MAEU9736478", + "scac": "MAEU" + }, + "relationships": { + "customer": { + "data": { + "id": "f7cb530a-9e60-412c-a5bc-205a2f34ba54", + "type": "party" + } + } + }, + "type": "tracking_request" + } + } + ``` + + **Notes**: + - The customer must first be created using the Parties API + - The customer ID is used to link the shipment to the specific customer + - This association enables filtering shipments by customer + - Customer information will be included in webhook notifications when using appropriate includes + - See the [Adding Customer guide](/api-docs/in-depth-guides/adding-customer) for more details + + + +## Tracking Request Lifecycle + +When you submit a tracking request, it goes through several possible states: + +Tracking Request Lifecycle + +### Status Transitions + + + + Initial state when a tracking request is first submitted. Terminal49 is processing your request and attempting to locate the shipment. + + + The shipment was successfully located and is now being tracked. A shipment object has been created. + + + Unable to locate the shipment after multiple attempts. Check the failed_reason for details. + + + The bill of lading number appears valid, but the shipment data isn't yet available in the carrier's system. Terminal49 will continue checking daily. + + + Tracking has been manually or automatically stopped for this request. + + + +### Common Failure Reasons + + + + The carrier could not locate the shipment using the provided reference number. + + **Troubleshooting:** + - Verify the reference number format and carrier code + - For new bookings or bills of lading, wait 24-48 hours and try again + - Check if the carrier supports the reference type you're using + + + + The shipment is already being tracked in Terminal49. + + **Troubleshooting:** + - Search for the existing shipment using the List Shipments endpoint + - Use filters to find the shipment by reference number + - Use the existing shipment data instead of creating a new tracking request + + + + The reference number format does not match what the carrier expects. + + **Troubleshooting:** + - Check for typos and correct formatting + - Ensure you're including any required prefixes + - Verify with shipping documents that the number is correct + + + + Terminal49 has attempted to locate the shipment multiple times without success. + + **Troubleshooting:** + - Verify the reference number and carrier are correct + - For new shipments, wait a few days and submit a new tracking request + - Consider using a different reference type if available + + + +For a complete list of failure reasons and detailed troubleshooting, see the [Tracking Request Lifecycle guide](/api-docs/in-depth-guides/tracking-request-lifecycle). + +## Tracking Request Webhooks + +Terminal49 sends webhook notifications at key points in the tracking request lifecycle: + + + + Sent when a tracking request successfully locates a shipment and begins tracking. + + **Payload includes:** + - Tracking request details (ID, reference number, type) + - Newly created shipment information + - Container details when included in the request + + **Common uses:** + - Update your systems to indicate active tracking + - Store the shipment ID for future API calls + - Notify internal teams that tracking has begun + + + + Sent when a tracking request fails to locate a shipment after all retry attempts. + + **Payload includes:** + - Tracking request details + - Failure reason code + - Detailed error message + + **Common uses:** + - Trigger alerts for manual investigation + - Implement automatic retry strategies based on failure reason + - Update internal systems with failure status + + + + Sent when a bill of lading number is valid but the shipment is not yet in the carrier's system. + + **Payload includes:** + - Tracking request details + - Status information + + **Common uses:** + - Update systems to show "pending" status + - Set reminders to check status in a few days + - Notify stakeholders about the delay + + + + Sent when tracking has been stopped for a request. + + **Payload includes:** + - Tracking request details + - Reason for stopping (if available) + + **Common uses:** + - Update internal systems + - Archive shipment records + - Trigger cleanup processes + + + +Learn more about setting up and managing webhooks in the [Webhooks guide](/api-docs/in-depth-guides/webhooks). + +## Managing Tracking Requests + +### Listing and Searching Tracking Requests + +Use the tracking requests index endpoint to retrieve existing tracking requests: + +``` +GET /v2/tracking_requests +``` + +Filter by various attributes: + +``` +GET /v2/tracking_requests?filter[status]=pending +GET /v2/tracking_requests?filter[request_number]=MAEU9736478 +GET /v2/tracking_requests?filter[created_at][gt]=2023-01-01T00:00:00Z +``` + +Sort results: + +``` +GET /v2/tracking_requests?sort=-created_at +``` + +Include related resources: + +``` +GET /v2/tracking_requests?include=shipment +``` + +### Starting and Stopping Tracking + +#### Starting Tracking + +To start tracking a new shipment, use the create tracking request endpoint: + +``` +POST /v2/tracking_requests +``` + +#### Stopping Tracking + +To stop tracking a shipment, use the stop tracking endpoint: + +``` +POST /v2/shipments/{shipment_id}/stop_tracking +``` + + + Stopping tracking means Terminal49 will no longer fetch updates for this shipment. This is useful for completed shipments or when you no longer need to monitor a shipment. + + +## Testing and Development + +Terminal49 provides test tracking numbers for development and integration testing: + + + + Use these special test numbers with the SCAC code "TEST" to simulate different scenarios: + + - `TEST-TR-SUCCEEDED` - Always succeeds and creates a test shipment + - `TEST-TR-FAILED` - Always fails with a "not_found" reason + - `TEST-TR-AWAITING-MANIFEST` - Simulates an awaiting manifest state + - `TEST-TR-DELAYED` - Simulates a delayed processing state before succeeding + + Example: + ```json + { + "data": { + "type": "tracking_request", + "attributes": { + "request_type": "bill_of_lading", + "request_number": "TEST-TR-SUCCEEDED", + "scac": "TEST" + } + } + } + ``` + + + + Terminal49 provides a sandbox environment for testing your integration without affecting production data: + + - Base URL: `https://sandbox-api.terminal49.com/v2` + - Requires a separate sandbox API key + - Simulates real-world scenarios with predictable outcomes + - Supports all the same endpoints as production + + Contact your Terminal49 account manager to request sandbox access. + + + +## Best Practices + +1. **Provide SCAC codes**: Always include the carrier's SCAC code to improve tracking success rates. + +2. **Use webhooks**: Set up webhook notifications instead of polling for status updates. + +3. **Handle retries**: Implement a reasonable retry strategy for failed tracking requests. + +4. **Track early**: Start tracking as soon as you have a booking number or bill of lading. + +5. **Add reference data**: Include your own reference numbers and tags to make shipments easily identifiable. + +6. **Test thoroughly**: Use the test tracking numbers to validate your integration. + +7. **Track by MBL when possible**: Master Bills of Lading generally provide the most reliable tracking. + +8. **Check carrier support**: Refer to the Carrier Data Matrix to verify which tracking methods are supported by each carrier. + +## Related Resources + + + + Detailed API documentation for the tracking request endpoint. + + + Learn how to configure webhooks to receive tracking updates. + + + See which carriers support which tracking methods. + + + Learn how to associate customers with tracking requests. + + diff --git a/docs/api-docs/in-depth-guides/authentication.mdx b/docs/api-docs/in-depth-guides/authentication.mdx new file mode 100644 index 00000000..26aa326d --- /dev/null +++ b/docs/api-docs/in-depth-guides/authentication.mdx @@ -0,0 +1,183 @@ +--- +title: Authentication +description: Learn how to authenticate with Terminal49's API and manage your API keys securely. +icon: "key" +--- + +# Authentication with Terminal49 + +This guide covers everything you need to know about authentication with Terminal49's API, including obtaining API keys, implementing authentication in your code, and security best practices. + +## How Terminal49 Authentication Works + +Terminal49 uses a token-based authentication system. Each request to the API must include an API key in the request headers to authenticate the request. + + + Authentication is required for all API endpoints except for the documentation and health check endpoints. + + +## Getting Your API Key + +To obtain an API key: + +1. Sign in to your Terminal49 account +2. Navigate to [Developer Settings](https://app.terminal49.com/developers/api-keys) +3. Click "Generate New API Key" +4. Name your key based on its intended use (e.g., "Production Integration" or "Development Testing") +5. Copy and securely store your API key - you won't be able to view it again + + + API keys grant access to your data and should be kept secure. Never share your API key or commit it to version control systems. + + +## Using Your API Key + +When making requests to the Terminal49 API, you need to include your API key in the `Authorization` header of each request with the prefix `Token`. + +``` +Authorization: Token YOUR_API_KEY +``` + +Here's how to include the Authorization header in different programming languages: + +```javascript title="JavaScript/Node.js" +// Using fetch API +const response = await fetch('https://api.terminal49.com/v2/shipments', { + headers: { + 'Authorization': 'Token YOUR_API_KEY', + 'Content-Type': 'application/vnd.api+json' + } +}); + +// Using Axios +const axios = require('axios'); +axios.get('https://api.terminal49.com/v2/shipments', { + headers: { + 'Authorization': 'Token YOUR_API_KEY', + 'Content-Type': 'application/vnd.api+json' + } +}); +``` + +```php title="PHP" +request('GET', 'https://api.terminal49.com/v2/shipments', [ + 'headers' => [ + 'Authorization' => 'Token YOUR_API_KEY', + 'Content-Type' => 'application/vnd.api+json' + ] +]); + +$data = json_decode($response->getBody(), true); +?> +``` + +```python title="Python" +import requests + +url = "https://api.terminal49.com/v2/shipments" +headers = { + "Authorization": "Token YOUR_API_KEY", + "Content-Type": "application/vnd.api+json" +} + +response = requests.get(url, headers=headers) +``` + +```ruby title="Ruby" +require 'net/http' +require 'uri' +require 'json' + +uri = URI.parse('https://api.terminal49.com/v2/shipments') +request = Net::HTTP::Get.new(uri) +request['Authorization'] = 'Token YOUR_API_KEY' +request['Content-Type'] = 'application/vnd.api+json' + +response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http| + http.request(request) +end +``` + +```csharp title="C#" +using System; +using System.Net.Http; +using System.Threading.Tasks; + +class Program +{ + static async Task Main() + { + using var client = new HttpClient(); + client.DefaultRequestHeaders.Add("Authorization", "Token YOUR_API_KEY"); + client.DefaultRequestHeaders.Add("Content-Type", "application/vnd.api+json"); + + var response = await client.GetAsync("https://api.terminal49.com/v2/shipments"); + var content = await response.Content.ReadAsStringAsync(); + Console.WriteLine(content); + } +} +``` + +## Troubleshooting Authentication Issues + +### Common Error Responses + +| HTTP Status | Message | Possible Cause | +|-------------|---------|----------------| +| 401 Unauthorized | "Invalid API Key" | The API key is incorrect or has been disabled | +| 401 Unauthorized | "Missing API Key" | No Authorization header was provided | +| 403 Forbidden | "Insufficient permissions" | The API key doesn't have permission for the requested action | + +### Debugging Tips + +1. **Check the Authorization header**: Ensure it's properly formatted as `Token YOUR_API_KEY` +2. **Verify the API key**: Check that you're using the correct API key for the environment +3. **Test with curl**: Try a simple curl request to isolate authentication issues + +```bash title="curl Example" +curl -H "Authorization: Token YOUR_API_KEY" \ + -H "Content-Type: application/vnd.api+json" \ + https://api.terminal49.com/v2/shipments +``` + +## Disabling API Keys + +If an API key is compromised or no longer needed: + +1. Sign in to your Terminal49 account +2. Navigate to [Developer Settings](https://app.terminal49.com/developers/api-keys) +3. Find the API key you want to disable +4. Click the "Disable Key" button (circular icon with a line through it) + + + ![Disable API Key location](/images/disable-key.png) + + + + Disabling an API key is immediate. Any applications using the key will lose access to the API. + + +## Next Steps + +- Learn about [Webhooks](/api-docs/in-depth-guides/webhooks) for real-time updates +- Explore the [JSON:API Guide](/api-docs/in-depth-guides/json-api-guide) to understand the API response format +- Start [Tracking Shipments and Containers](/api-docs/getting-started/tracking-shipments-and-containers) diff --git a/docs/api-docs/in-depth-guides/error-handling.mdx b/docs/api-docs/in-depth-guides/error-handling.mdx new file mode 100644 index 00000000..74bc08cc --- /dev/null +++ b/docs/api-docs/in-depth-guides/error-handling.mdx @@ -0,0 +1,334 @@ +--- +title: Error Handling +description: Learn how to effectively handle and troubleshoot errors in Terminal49's API +icon: "triangle-exclamation" +--- + +# Error Handling and Troubleshooting + +This guide explains how Terminal49's API reports errors and provides strategies for troubleshooting and handling them effectively in your applications. + +## Understanding Error Responses + +Terminal49's API follows the JSON:API specification for error responses. When an error occurs, the API returns: + +1. An appropriate HTTP status code +2. A JSON response with detailed error information + +```json +{ + "errors": [ + { + "status": "422", + "title": "Invalid attribute", + "detail": "The SCAC 'UNKNOWN' is not supported.", + "source": { + "pointer": "/data/attributes/scac" + }, + "code": "unsupported_scac" + } + ] +} +``` + +### Error Object Properties + +Each error object in the `errors` array includes: + + + The HTTP status code for this particular error (as a string). + + + + A short, human-readable summary of the problem. + + + + A human-readable explanation with specific details about the error. + + + + Information about the source of the error: + - `pointer`: A JSON Pointer to the specific field that caused the error (e.g., "/data/attributes/scac") + + + + A machine-readable error code that can be used for programmatic handling (not always present). + + +## Common HTTP Status Codes + +| Status Code | Meaning | Typical Causes | +|-------------|---------|----------------| +| 400 Bad Request | The request was malformed or invalid | Invalid JSON, missing required fields | +| 401 Unauthorized | Authentication failed | Invalid or missing API key | +| 403 Forbidden | Permission denied | Your API key doesn't have access to the requested resource | +| 404 Not Found | Resource not found | The requested ID doesn't exist | +| 422 Unprocessable Entity | Validation errors | Input validation failed | +| 429 Too Many Requests | Rate limit exceeded | You've made too many requests in a short period | +| 500, 502, 503 | Server errors | Problem on Terminal49's side | + +## Common Error Types and Solutions + + + + **Example:** + ```json + { + "errors": [ + { + "status": "401", + "title": "Unauthorized", + "detail": "Invalid API key provided" + } + ] + } + ``` + + **Solutions:** + 1. Check that you're including the `Authorization` header with the correct format: `Token YOUR_API_KEY` + 2. Verify your API key is active in the [Developer Settings](https://app.terminal49.com/developers/api-keys) + 3. Make sure the API key hasn't been disabled + + + + **Example:** + ```json + { + "errors": [ + { + "status": "422", + "source": { + "pointer": "/data/attributes/request_number" + }, + "title": "Unprocessable Entity", + "detail": "Request number 'ABCD1234567' with scac 'MAEU' already exists in a tracking_request with a pending or created status", + "code": "duplicate" + } + ] + } + ``` + + **Solutions:** + 1. Check the `pointer` field to identify which attribute caused the error + 2. Review the `detail` message for specific information about the validation failure + 3. For duplicate tracking requests, check if the shipment is already being tracked + 4. Verify SCAC codes against the [supported carriers list](https://app.terminal49.com/developers/carrier-data) + + + + **Example:** + ```json + { + "errors": [ + { + "status": "404", + "title": "Not Found", + "detail": "Couldn't find Shipment with ID=123e4567-e89b-12d3-a456-426614174000" + } + ] + } + ``` + + **Solutions:** + 1. Verify the ID you're using exists and is correctly formatted + 2. Check if the resource might have been deleted + 3. Confirm you're using the correct endpoint for the resource type + + + + **Example:** + ```json + { + "errors": [ + { + "status": "429", + "title": "Too Many Requests", + "detail": "Rate limit exceeded. Please wait and try again later." + } + ] + } + ``` + + **Solutions:** + 1. Implement retry logic with exponential backoff + 2. Spread requests over a longer period + 3. Contact Terminal49 support if you need a higher rate limit for your use case + + + +## Common Error Scenarios + +### Duplicate Tracking Requests + +**Error:** +```json +{ + "errors": [ + { + "status": "422", + "source": { + "pointer": "/data/attributes/request_number" + }, + "title": "Unprocessable Entity", + "detail": "Request number 'MAEU1234567' with scac 'MAEU' already exists in a tracking_request with a pending or created status", + "code": "duplicate" + } + ] +} +``` + +**Solution:** +When you receive a duplicate error, you should: +1. Look up the existing tracking request using the `GET /tracking_requests` endpoint with appropriate filters +2. Use the ID of the existing request to fetch its current status +3. If needed, you can update the existing tracking request rather than creating a new one + +### Unsupported Carrier + +**Error:** +```json +{ + "errors": [ + { + "status": "422", + "source": { + "pointer": "/data/attributes/scac" + }, + "title": "Unprocessable Entity", + "detail": "Scac 'ABCD' is not supported", + "code": "unsupported_scac" + } + ] +} +``` + +**Solution:** +1. Reference the [Carrier Data Matrix](https://app.terminal49.com/developers/carrier-data) for a list of supported carriers +2. Double-check the SCAC code format (usually 4 uppercase letters) +3. If you need support for a specific carrier, contact Terminal49 + +## Implementing Effective Error Handling + +### JavaScript Example + +```javascript +async function makeApiRequest(endpoint, method = 'GET', data = null) { + try { + const options = { + method, + headers: { + 'Authorization': `Token ${API_KEY}`, + 'Content-Type': 'application/vnd.api+json' + } + }; + + if (data && (method === 'POST' || method === 'PATCH')) { + options.body = JSON.stringify(data); + } + + const response = await fetch(`https://api.terminal49.com/v2/${endpoint}`, options); + const responseData = await response.json(); + + // Check if the response contains errors + if (!response.ok) { + // Process errors + if (responseData.errors) { + const error = responseData.errors[0]; + + switch (response.status) { + case 401: + throw new Error(`Authentication error: ${error.detail}`); + case 404: + throw new Error(`Resource not found: ${error.detail}`); + case 422: + const field = error.source?.pointer?.split('/').pop() || 'unknown field'; + throw new Error(`Validation error in ${field}: ${error.detail}`); + case 429: + // Implement retry with exponential backoff + throw new Error(`Rate limit exceeded: ${error.detail}`); + default: + throw new Error(`API error ${response.status}: ${error.detail}`); + } + } + + throw new Error(`API request failed with status ${response.status}`); + } + + return responseData; + } catch (error) { + console.error('API request failed:', error); + throw error; + } +} +``` + +## Retry Strategy + +When dealing with rate limiting or transient errors, implementing a retry strategy with exponential backoff is essential: + +```javascript +async function makeRequestWithRetry(endpoint, method = 'GET', data = null, maxRetries = 3) { + let retries = 0; + + while (retries < maxRetries) { + try { + return await makeApiRequest(endpoint, method, data); + } catch (error) { + retries++; + + // If it's not a retriable error or we've used all retries, throw + if ( + !error.message.includes('Rate limit exceeded') || + retries >= maxRetries + ) { + throw error; + } + + // Exponential backoff with jitter + const delay = Math.min(1000 * 2 ** retries + Math.random() * 1000, 60000); + console.log(`Retrying after ${delay}ms (attempt ${retries} of ${maxRetries})...`); + await new Promise(resolve => setTimeout(resolve, delay)); + } + } +} +``` + +## Best Practices for Error Handling + +1. **Always check for error responses** + - Parse the response body to look for the `errors` array + - Handle both HTTP errors and JSON:API formatted errors + +2. **Log detailed error information** + - Include the status code, error title, error detail, and any source information + - Add context from your application (e.g., what operation was being attempted) + +3. **Implement appropriate retry logic** + - Use exponential backoff for rate limiting (429) errors + - Don't retry for validation (422) or authentication (401) errors + +4. **Present user-friendly error messages** + - Translate technical API errors to understandable messages for end-users + - Provide actionable guidance when possible + +5. **Monitor error rates** + - Set up alerts for unusual error patterns + - Keep track of error frequency to identify potential issues with your integration + +## Getting Help + +If you encounter persistent errors that you can't resolve: + +1. Check the [API Documentation](https://api.docs.terminal49.com/) for endpoint-specific requirements +2. Ensure you're using the latest version of the API +3. Contact Terminal49 support at [support@terminal49.com](mailto:support@terminal49.com) with: + - The full error response (with headers if possible) + - A description of what you were trying to do + - Any relevant request IDs or resource IDs + +## Next Steps + +- Learn about [Authentication](/api-docs/in-depth-guides/authentication) to avoid auth-related errors +- Explore the [JSON:API Guide](/api-docs/in-depth-guides/json-api-guide) to better understand API responses +- Read about [Webhooks](/api-docs/in-depth-guides/webhooks) for asynchronous event handling diff --git a/docs/api-docs/in-depth-guides/json-api-guide.mdx b/docs/api-docs/in-depth-guides/json-api-guide.mdx new file mode 100644 index 00000000..87360106 --- /dev/null +++ b/docs/api-docs/in-depth-guides/json-api-guide.mdx @@ -0,0 +1,330 @@ +--- +title: Understanding JSON:API +description: Learn the basics of JSON:API and how it's implemented in Terminal49's API. +--- + +# JSON:API Guide + +Terminal49's API follows the [JSON:API specification](https://jsonapi.org/), a standard for building APIs in JSON. This guide will help you understand the key concepts and how to work with our API effectively. + +## What is JSON:API? + +JSON:API is a specification for how a client should request resources to be fetched or modified, and how a server should respond. It's designed to minimize both the number of requests and the amount of data transmitted between clients and servers. + + + The Terminal49 API conforms to the JSON:API v1.0 specification. This standardization makes our API more predictable and easier to work with once you understand the patterns. + + +## Why Terminal49 Uses JSON:API + +Terminal49 uses the JSON:API specification for a number of important reasons that directly benefit developers working with shipping and container tracking data: + +### Standardized Relationships for Complex Shipping Data + +Shipping data is inherently relational - containers belong to shipments, shipments have transport events, containers have tracking events, and all these relate to carriers, terminals, and vessels. JSON:API excels at representing these complex relationships through: + +- **Consistent Structure**: Every response follows the same pattern, making data predictable and easy to parse +- **Clear Resource Relationships**: Explicit definition of how resources like shipments, containers, and events relate to each other +- **Compound Documents**: The ability to include related resources in a single request, eliminating the need for multiple API calls + +### Efficient Data Loading for Shipping Operations + +JSON:API's approach to related data is particularly valuable for shipping operations: + +```json +{ + "data": { + "id": "123", + "type": "shipment", + "attributes": { + "bill_of_lading_number": "MAEU1234567", + "carrier_name": "Maersk" + }, + "relationships": { + "containers": { + "data": [ + { "id": "456", "type": "container" } + ] + } + } + }, + "included": [ + { + "id": "456", + "type": "container", + "attributes": { + "number": "MRKU1234567", + "size_type": "40HC" + } + } + ] +} +``` + +This allows Terminal49 to deliver a complete picture of your shipping data in fewer requests, which means: + +- **Reduced API Calls**: Get containers and their shipments in a single request +- **Minimized Latency**: Critical for time-sensitive logistics operations +- **Lower Bandwidth**: Only request the related objects you need, when you need them + +### Standardized Error Handling + +JSON:API provides a consistent format for error responses, making it easier to: + +- **Identify Issues**: Clearly pinpoint which field or relationship has an error +- **Implement Error Handling**: Write consistent error handling logic across your application +- **Debug Problems**: Receive detailed information about what went wrong and why + +## Client Libraries + +Instead of writing custom code to parse JSON:API responses, you can leverage ready-made libraries for most programming languages: + + + + JSON:API libraries for JavaScript + + + jsonapi-rb for Ruby applications + + + JSON:API libraries for Python + + + PHP client libraries for JSON:API + + + Go implementations of JSON:API + + + Java frameworks for JSON:API + + + +These libraries provide significant benefits when working with Terminal49's API: + +- **Automatic Relationship Resolution**: Easily navigate from shipments to containers and events without manual mapping +- **Type Conversion**: Convert API responses into native language objects/models +- **Query Building**: Construct complex API queries with simple, intuitive methods +- **Consistent Updates**: Standardized methods for creating and updating resources +- **Error Handling**: Built-in support for JSON:API error formats + +Using these libraries can reduce development time by up to 50% when working with complex shipping data structures, allowing you to focus on your business logic rather than API parsing details. + +## JSON:API Structure + +### Basic Resource Objects + +All Terminal49 API responses follow this general structure: + +```json +{ + "data": { + "id": "123", + "type": "shipment", + "attributes": { + "bill_of_lading_number": "MAEU9736478", + "created_at": "2023-05-15T14:23:12Z" + // Other shipment attributes + }, + "relationships": { + "containers": { + "data": [ + { "id": "456", "type": "container" }, + { "id": "789", "type": "container" } + ] + } + } + } +} +``` + +The main components are: + + + The primary resource or collection of resources. + + + + A unique identifier for the resource. + + + + The resource type (e.g., "shipment", "container", "tracking_request"). + + + + The resource's properties (e.g., numbers, dates, status values). + + + + References to related resources (e.g., a shipment's containers). + + +### Collections of Resources + +When requesting multiple resources, the response structure is: + +```json +{ + "data": [ + { + "id": "123", + "type": "shipment", + "attributes": { ... } + }, + { + "id": "124", + "type": "shipment", + "attributes": { ... } + } + ], + "meta": { + "total_count": 45 + }, + "links": { + "self": "https://api.terminal49.com/v2/shipments?page[number]=1&page[size]=2", + "next": "https://api.terminal49.com/v2/shipments?page[number]=2&page[size]=2" + } +} +``` + + + Contains non-standard meta-information, like counts or pagination details. + + + + Contains links for pagination or related operations. + + +## Working with JSON:API in Terminal49 + +### Creating Resources + +When creating a resource (like a tracking request), your request should follow this format: + +```json +{ + "data": { + "type": "tracking_request", + "attributes": { + "request_type": "bill_of_lading", + "request_number": "MAEU9736478", + "scac": "MAEU" + } + } +} +``` + +Notice that: +- The `type` field is required and must match the expected resource type +- All properties go inside the `attributes` object +- You don't specify an `id` for new resources (the server assigns it) + +### Including Related Resources + +One powerful feature of JSON:API is the ability to include related resources in a single request using the `include` parameter: + +``` +GET /shipments/123?include=containers,containers.transport_events +``` + +This returns the shipment with its containers and each container's transport events in one response: + +```json +{ + "data": { + "id": "123", + "type": "shipment", + "attributes": { ... }, + "relationships": { + "containers": { + "data": [ + { "id": "456", "type": "container" } + ] + } + } + }, + "included": [ + { + "id": "456", + "type": "container", + "attributes": { ... }, + "relationships": { + "transport_events": { + "data": [ + { "id": "789", "type": "transport_event" } + ] + } + } + }, + { + "id": "789", + "type": "transport_event", + "attributes": { ... } + } + ] +} +``` + + + Contains all the related resources referenced in the main resource's relationships. + + +### Error Handling + +Error responses also follow a standard format: + +```json +{ + "errors": [ + { + "status": "422", + "title": "Invalid attribute", + "detail": "The SCAC 'UNKNOWN' is not supported.", + "source": { + "pointer": "/data/attributes/scac" + } + } + ] +} +``` + +Each error object contains: + + + The HTTP status code relevant to the error. + + + + A short, human-readable summary of the problem. + + + + A human-readable explanation of the specific error. + + + + An object containing references to the source of the error. The `pointer` field typically contains a JSON Pointer to the specific field that caused the error. + + +## Tips for Working with Terminal49's JSON:API + +- Always include the correct `Content-Type` header in your requests: + ``` + Content-Type: application/vnd.api+json + ``` + +- Use the `include` parameter to reduce the number of API calls when you need related resources + +- When creating or updating resources, remember that all properties must be inside the `attributes` object + +- For filtering collections, use the filter query parameter with the attribute name: + ``` + GET /containers?filter[status]=arrived + ``` + +- Pagination is handled via `page[number]` and `page[size]` query parameters + + + For a deeper understanding of JSON:API, check the [official specification](https://jsonapi.org/format/) or our [In-Depth Guides](/api-docs/in-depth-guides/including-resources) on working with Terminal49's API. + diff --git a/docs/api-docs/in-depth-guides/polling-vs-webhooks.mdx b/docs/api-docs/in-depth-guides/polling-vs-webhooks.mdx new file mode 100644 index 00000000..e93f929d --- /dev/null +++ b/docs/api-docs/in-depth-guides/polling-vs-webhooks.mdx @@ -0,0 +1,270 @@ +--- +title: Polling vs. Webhooks +description: Compare the two approaches for receiving updates from Terminal49's API and choose the best one for your integration. +--- + +# Polling vs. Webhooks in Terminal49 + +There are two primary methods for getting updates from Terminal49's API: **polling** and **webhooks**. This guide explains both approaches, their trade-offs, and helps you choose the best option for your integration. + +## Understanding Both Approaches + + + + Your system periodically makes API requests to check for updates. + + + Terminal49 proactively sends updates to your system when changes occur. + + + +## Polling: The Pull Approach + +Polling involves your application regularly querying the Terminal49 API to check for updates on your tracked shipments and containers. + +```javascript title="Polling Example" +// Example of polling in JavaScript +async function pollForUpdates() { + try { + const response = await fetch('https://api.terminal49.com/v2/containers/ABC123', { + headers: { + 'Authorization': 'Token YOUR_API_KEY' + } + }); + const container = await response.json(); + + // Process container data + updateContainerStatus(container); + + } catch (error) { + console.error('Error polling for updates:', error); + } +} + +// Poll every 15 minutes +setInterval(pollForUpdates, 15 * 60 * 1000); +``` + +### When to Use Polling + + + Polling may be easier to implement initially, especially if you're working with a system that doesn't support receiving external HTTP requests or if you're behind a firewall that blocks incoming traffic. + + + + During development or for simple proof-of-concept integrations, polling can be a quick way to get started. + + + + If you only need to check container status once or twice a day, polling might be sufficient. + + +### Disadvantages of Polling + + + Polling can lead to delayed notifications, excessive API usage, and missed updates if your polling interval is too long. + + +- **Delayed Updates**: You only receive updates when you poll, which could be minutes or hours after an event occurred. +- **Increased API Usage**: Frequent polling consumes more API resources. +- **Potential Rate Limiting**: Very frequent polling could trigger rate limits. +- **Missed Events**: If you poll infrequently, you might miss intermediate state changes. + +## Webhooks: The Push Approach + +Webhooks allow Terminal49 to push real-time updates to your system whenever a change occurs, without you having to ask for them. + +```javascript title="Webhook Setup" +// Creating a webhook endpoint in Node.js (Express) +const express = require('express'); +const app = express(); +app.use(express.json()); + +app.post('/terminal49-webhook', (req, res) => { + const event = req.body.data.attributes.event; + const reference = req.body.data.relationships.reference_object.data; + + console.log(`Received ${event} for ${reference.type} ${reference.id}`); + + // Process the webhook notification + processWebhookEvent(req.body); + + // Always return a success response + res.status(200).send('Webhook received'); +}); + +app.listen(3000, () => { + console.log('Webhook server running on port 3000'); +}); +``` + +```javascript title="Webhook Registration" +// Registering a webhook with Terminal49 API +async function registerWebhook() { + try { + const response = await fetch('https://api.terminal49.com/v2/webhooks', { + method: 'POST', + headers: { + 'Authorization': 'Token YOUR_API_KEY', + 'Content-Type': 'application/vnd.api+json' + }, + body: JSON.stringify({ + data: { + type: "webhook", + attributes: { + url: "https://your-domain.com/terminal49-webhook", + event_types: [ + "container.transport.vessel_arrived", + "container.transport.vessel_discharged", + "container.pickup_lfd.changed", + "tracking_request.succeeded", + "tracking_request.failed" + ] + } + } + }) + }); + + const webhook = await response.json(); + console.log('Webhook registered:', webhook); + + } catch (error) { + console.error('Error registering webhook:', error); + } +} +``` + +### When to Use Webhooks + + + If you need immediate notifications about changes (like container arrivals, customs holds, or last free day changes), webhooks are essential. + + + + For production-grade integrations, webhooks provide the most reliable and efficient way to receive updates. + + + + If you're tracking many containers, webhooks prevent you from having to poll the status of each one repeatedly. + + +### Implementing Webhooks Successfully + +For webhooks to work correctly, you need: + +1. **A publicly accessible endpoint** that can receive HTTP POST requests +2. **Proper webhook handling** including: + - Responding with HTTP 200, 201, 202, or 204 status codes + - Processing webhook notifications asynchronously when possible + - Proper error handling and retry logic +3. **Security considerations**: + - Validate the webhook payload + - Use HTTPS for your endpoint + - Consider IP filtering if available + + + During development, you can use tools like [ngrok](https://ngrok.com/) to expose your local development environment to the internet for testing webhooks. + + +## Recommended Approach: Webhooks with Polling Fallback + +The recommended approach for Terminal49 integration is to: + +1. **Primary: Set up webhooks** for real-time notifications +2. **Secondary: Implement polling** as a fallback to verify data or recover from missed webhooks + + + ![Webhook with Polling Fallback](/images/webhook-with-polling-fallback.png) + + +This hybrid approach ensures you get the benefits of real-time updates while having a safety net to verify data integrity. + +## Example: Hybrid Implementation + +```javascript title="Hybrid Approach" +// 1. Set up webhooks (primary method) +// See webhook registration example above + +// 2. Implement periodic verification (fallback) +async function verifyContainerStatus() { + // Poll at a much lower frequency (e.g., once per day) + // to verify data and catch any potentially missed webhooks + try { + const response = await fetch('https://api.terminal49.com/v2/containers?filter[updated_since]=24h', { + headers: { + 'Authorization': 'Token YOUR_API_KEY' + } + }); + + const data = await response.json(); + const containers = data.data; + + // Verify each container against your local database + for (const container of containers) { + verifyContainerData(container); + } + + } catch (error) { + console.error('Error verifying container status:', error); + } +} + +// Run verification once per day +setInterval(verifyContainerStatus, 24 * 60 * 60 * 1000); +``` + +## Summary Comparison + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
FeaturePollingWebhooks
Update TimelinessDelayed (depends on polling interval)Real-time
API UsageHigher (regular requests)Lower (on-demand updates)
Implementation ComplexitySimpleModerate (requires public endpoint)
Server RequirementsNone (client-initiated)Public HTTP endpoint
Good ForDevelopment, testing, simple use casesProduction systems, real-time requirements
+ +## Next Steps + + + + Learn how to implement webhooks for real-time updates. + + + Understand how Terminal49 handles webhook delivery retries. + + + Follow our recommended practices for efficient API usage. + + + Use test tracking numbers to simulate events for webhook testing. + + diff --git a/docs/api-docs/in-depth-guides/quickstart.mdx b/docs/api-docs/in-depth-guides/quickstart.mdx index c377a5db..4b7e9606 100644 --- a/docs/api-docs/in-depth-guides/quickstart.mdx +++ b/docs/api-docs/in-depth-guides/quickstart.mdx @@ -1,125 +1,461 @@ --- -title: Quick Start Guide -og:title: Quickstart Guide | Terminal49 API Documentation -og:description: Get started quickly with Terminal49's API using this comprehensive guide. Simplify your shipping workflows today. +title: Quickstart +description: Get up and running with Terminal49's API in minutes - track containers, receive updates, and integrate with your systems. +icon: "rocket" --- -## Before You Begin +This guide will help you get started with Terminal49's API by walking through the most common workflow: tracking a shipment and receiving status updates. You'll learn both how to request data and how to receive real-time updates via webhooks. -You'll need a four things to get started. +## Prerequisites -1. **A Bill of Lading (BOL) number.** This is issued by your carrier. BOL numbers are found on your [bill of lading](https://en.wikipedia.org/wiki/Bill_of_lading) document. Ideally, this will be a shipment that is currently on the water or in terminal, but this is not necessary. -2. **The SCAC of the carrier that issued your bill of lading.** The Standard Carrier Alpha Code of your carrier is used to identify carriers in computer systems and in shipping documents. You can learn more about these [here](https://en.wikipedia.org/wiki/Standard_Carrier_Alpha_Code). -3. **A Terminal49 Account.** If you don't have one yet, [sign up here.](https://app.terminal49.com/register) -4. **An API key.** Sign in to your Terminal49 account and go to your [developer portal page](https://app.terminal49.com/developers) to get your API key. +Before you begin, you'll need: -## Track a Shipment +1. **A Terminal49 account** - [Sign up here](https://app.terminal49.com/register) if you don't have one +2. **An API key** - Find or create one in your [developer portal](https://app.terminal49.com/developers/api-keys) +3. **A tracking number** to monitor: + - Bill of Lading (BOL) number, Booking number, or Container number + - The [SCAC](https://en.wikipedia.org/wiki/Standard_Carrier_Alpha_Code) of the carrier (e.g., MAEU for Maersk) -You can try this using the embedded request maker below, or using Postman. + + Don't have a tracking number? Use our test data: `TEST-TR-SUCCEEDED` with SCAC `TEST` for immediate results. + -1. Try it below. Click "Headers" and replace YOUR_API_KEY with your API key. In the authorization header value. -2. Enter a value for the `request_number` and `scac`. The request number has to be a shipping line booking or master bill of lading number. The SCAC has to be a shipping line scac (see data sources to get a list of valid SCACs) +## Step 1: Authentication -Note that you can also access sample code, include a cURL template, by clicking the "Code Generation" tab in the Request Maker. +All API requests require authentication using your API key. Include it in the Authorization header with the prefix `Token`: -```json http -{ - "method": "post", - "url": "https://api.terminal49.com/v2/tracking_requests", - "headers": { - "Content-Type": "application/vnd.api+json", - "Authorization": "Token YOUR_API_KEY" - }, - "body": "{\r\n \"data\": {\r\n \"attributes\": {\r\n \"request_type\": \"bill_of_lading\",\r\n \"request_number\": \"\",\r\n \"scac\": \"\"\r\n },\r\n \"type\": \"tracking_request\"\r\n }\r\n}" -} +```bash +curl -X GET "https://api.terminal49.com/v2/shipments" \ + -H "Authorization: Token YOUR_API_KEY" \ + -H "Content-Type: application/vnd.api+json" ``` -## Check Your Tracking Request Succeeded + +```javascript +// Node.js with fetch +const headers = { + 'Authorization': `Token ${YOUR_API_KEY}`, + 'Content-Type': 'application/vnd.api+json' +}; + +const response = await fetch('https://api.terminal49.com/v2/shipments', { headers }); +``` + +```php +// PHP with cURL +$curl = curl_init(); +curl_setopt_array($curl, [ + CURLOPT_URL => "https://api.terminal49.com/v2/shipments", + CURLOPT_RETURNTRANSFER => true, + CURLOPT_HTTPHEADER => [ + "Authorization: Token YOUR_API_KEY", + "Content-Type: application/vnd.api+json" + ], +]); +$response = curl_exec($curl); +``` -We have not yet set up a webook to receive status updates from the Terminal49 API, so we will need to manually poll to check if the Tracking Request has succeeded or failed. +```ruby +# Ruby with HTTParty +require 'httparty' +headers = { + 'Authorization' => "Token YOUR_API_KEY", + 'Content-Type' => 'application/vnd.api+json' +} +response = HTTParty.get("https://api.terminal49.com/v2/shipments", headers: headers) +``` + + +## Step 2: Create a Tracking Request + +The first step to track a shipment is to create a tracking request. This tells Terminal49 which shipment you want to track. + +**Workflow Example: Start tracking a container to get its ETA** + + +```bash +# Using cURL +curl -X POST "https://api.terminal49.com/v2/tracking_requests" \ + -H "Authorization: Token YOUR_API_KEY" \ + -H "Content-Type: application/vnd.api+json" \ + -d '{ + "data": { + "type": "tracking_request", + "attributes": { + "request_type": "bill_of_lading", + "request_number": "MAEU9736478", + "scac": "MAEU" + } + } + }' +``` -> ### Tracking Request Troubleshooting -> The most common issue people encounter is that they are entering the wrong number. -> -> Please check that you are entering the Bill of Lading number, booking number, or container number and not internal reference at your company or by your frieght forwarder. You can the number you are supplying by going to a carrier's website and using their tools to track your shipment using the request number. If this works, and if the SCAC is supported by T49, you should able to track it with us. -> -> You can always email us at support@terminal49.com if you have persistent issues. +```javascript +// Using fetch (Node.js/Browser) +const response = await fetch('https://api.terminal49.com/v2/tracking_requests', { + method: 'POST', + headers: { + 'Authorization': `Token ${YOUR_API_KEY}`, + 'Content-Type': 'application/vnd.api+json' + }, + body: JSON.stringify({ + data: { + type: 'tracking_request', + attributes: { + request_type: 'bill_of_lading', + request_number: 'MAEU9736478', + scac: 'MAEU' + } + } + }) +}); + +const result = await response.json(); +console.log(result); +``` -** Try it below. Click "Headers" and replace `` with your API key.** +```php +// Using PHP +$curl = curl_init(); +$payload = json_encode([ + 'data' => [ + 'type' => 'tracking_request', + 'attributes' => [ + 'request_type' => 'bill_of_lading', + 'request_number' => 'MAEU9736478', + 'scac' => 'MAEU' + ] + ] +]); + +curl_setopt_array($curl, [ + CURLOPT_URL => "https://api.terminal49.com/v2/tracking_requests", + CURLOPT_RETURNTRANSFER => true, + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => $payload, + CURLOPT_HTTPHEADER => [ + "Authorization: Token YOUR_API_KEY", + "Content-Type: application/vnd.api+json" + ], +]); + +$response = curl_exec($curl); +$result = json_decode($response, true); +print_r($result); +``` + +The response will include a tracking request ID and status: -```json http +```json { - "method": "get", - "url": "https://api.terminal49.com/v2/tracking_requests", - "headers": { - "Content-Type": "application/vnd.api+json", - "Authorization": "Token YOUR_API_KEY" + "data": { + "id": "b1f30140-c9cb-4354-b8e3-3d92a76289cf", + "type": "tracking_request", + "attributes": { + "request_type": "bill_of_lading", + "request_number": "MAEU9736478", + "scac": "MAEU", + "status": "pending", + "created_at": "2023-03-16T18:35:42Z", + "updated_at": "2023-03-16T18:35:42Z" + }, + "relationships": { + "tracked_object": { + "data": null + } + } } } ``` -## List your Tracked Shipments + + The tracking request starts with status `pending`. Terminal49 will process it asynchronously, and it will change to `succeeded` or `failed`. + -If your tracking request was successful, you will now be able to list your tracked shipments. +## Step 3: Check Tracking Request Status (Polling Method) -**Try it below. Click "Headers" and replace YOUR_API_KEY with your API key.** +To check if your tracking request has completed, you can poll the tracking request: -Sometimes it may take a while for the tracking request to show up, but usually no more than a few minutes. + +```bash +# Using cURL +curl -X GET "https://api.terminal49.com/v2/tracking_requests/b1f30140-c9cb-4354-b8e3-3d92a76289cf" \ + -H "Authorization: Token YOUR_API_KEY" \ + -H "Content-Type: application/vnd.api+json" +``` -If you had trouble adding your first shipment, try adding a few more. +```javascript +// Using fetch +const trackingRequestId = 'b1f30140-c9cb-4354-b8e3-3d92a76289cf'; +const response = await fetch(`https://api.terminal49.com/v2/tracking_requests/${trackingRequestId}`, { + headers: { + 'Authorization': `Token ${YOUR_API_KEY}`, + 'Content-Type': 'application/vnd.api+json' + } +}); + +const result = await response.json(); +console.log(`Tracking request status: ${result.data.attributes.status}`); +``` + + +When the request succeeds, it will include a relationship to the newly created shipment: -```json http +```json { - "method": "get", - "url": "https://api.terminal49.com/v2/shipments", - "headers": { - "Content-Type": "application/vnd.api+json", - "Authorization": "Token YOUR_API_KEY" + "data": { + "id": "b1f30140-c9cb-4354-b8e3-3d92a76289cf", + "type": "tracking_request", + "attributes": { + "request_type": "bill_of_lading", + "request_number": "MAEU9736478", + "scac": "MAEU", + "status": "succeeded", + "created_at": "2023-03-16T18:35:42Z", + "updated_at": "2023-03-16T18:36:10Z" + }, + "relationships": { + "tracked_object": { + "data": { + "id": "7a8f8d5e-4c7d-4f8c-9b2a-3c6d8e9f0a1b", + "type": "shipment" + } + } + } } } ``` -## List all your Tracked Containers +## Step 4: Get Shipment Details and ETA -You can also list out all of your containers, if you'd like to track at that level. +Once the tracking request has succeeded, you can retrieve the shipment and its estimated arrival time: -Try it after replacing `` with your API key. + +```bash +# Using cURL +curl -X GET "https://api.terminal49.com/v2/shipments/7a8f8d5e-4c7d-4f8c-9b2a-3c6d8e9f0a1b?include=containers" \ + -H "Authorization: Token YOUR_API_KEY" \ + -H "Content-Type: application/vnd.api+json" +``` -```json http -{ - "method": "get", - "url": "https://api.terminal49.com/v2/containers", - "headers": { - "Content-Type": "application/vnd.api+json", - "Authorization": "Token YOUR_API_KEY" +```javascript +// Using fetch +const shipmentId = '7a8f8d5e-4c7d-4f8c-9b2a-3c6d8e9f0a1b'; +const response = await fetch(`https://api.terminal49.com/v2/shipments/${shipmentId}?include=containers`, { + headers: { + 'Authorization': `Token ${YOUR_API_KEY}`, + 'Content-Type': 'application/vnd.api+json' } +}); + +const result = await response.json(); +console.log(`ETA: ${result.data.attributes.estimated_port_arrival_date}`); +``` + + +The response includes shipment details with the ETA: + +```json +{ + "data": { + "id": "7a8f8d5e-4c7d-4f8c-9b2a-3c6d8e9f0a1b", + "type": "shipment", + "attributes": { + "bill_of_lading_number": "MAEU9736478", + "booking_number": "123456789", + "shipping_line_scac": "MAEU", + "shipping_line_name": "Maersk", + "estimated_port_arrival_date": "2023-04-15T14:00:00Z", + "port_of_lading_name": "Shanghai", + "port_of_lading_locode": "CNSHA", + "port_of_discharge_name": "Los Angeles", + "port_of_discharge_locode": "USLAX", + "vessel_name": "MAERSK SEVILLE", + "voyage": "243E" + // ...other attributes + }, + "relationships": { + "containers": { + "data": [ + { + "id": "e5g4h8i2-4f8c-9b2a-3c6d8e9f0a1b", + "type": "container" + } + ] + } + } + }, + "included": [ + { + "id": "e5g4h8i2-4f8c-9b2a-3c6d8e9f0a1b", + "type": "container", + "attributes": { + "number": "MAEU1234567", + "size_type": "40HC", + "status": "In Transit", + "location": "At Sea", + "last_free_day": "2023-04-22T23:59:59Z" + // ...other attributes + } + } + ] } ``` +## Step 5: Set Up Webhooks for Real-Time Updates (Event-Driven Method) + +**The Recommended Approach**: Instead of constantly polling for updates, use webhooks to receive notifications when events occur. + +1. Set up a webhook endpoint on your server to receive POST requests +2. Register your webhook with Terminal49: + + +```bash +# Using cURL +curl -X POST "https://api.terminal49.com/v2/webhooks" \ + -H "Authorization: Token YOUR_API_KEY" \ + -H "Content-Type: application/vnd.api+json" \ + -d '{ + "data": { + "type": "webhook", + "attributes": { + "url": "https://your-server.com/webhook", + "active": true, + "events": ["*"] + } + } + }' +``` -## Listening for Updates with Webhooks +```javascript +// Using fetch +const response = await fetch('https://api.terminal49.com/v2/webhooks', { + method: 'POST', + headers: { + 'Authorization': `Token ${YOUR_API_KEY}`, + 'Content-Type': 'application/vnd.api+json' + }, + body: JSON.stringify({ + data: { + type: 'webhook', + attributes: { + url: 'https://your-server.com/webhook', + active: true, + events: ['*'] // Or specify individual events like 'shipment.estimated_arrival_change' + } + } + }) +}); + +const result = await response.json(); +console.log(`Webhook created with ID: ${result.data.id}`); +``` + + +3. Implement a webhook handler on your server: + + +```javascript +// Node.js with Express +const express = require('express'); +const app = express(); +app.use(express.json()); + +app.post('/webhook', (req, res) => { + const event = req.body; + + console.log(`Received event: ${event.data.id}`); + console.log(`Event type: ${event.type}`); + + // Handle different event types + if (event.type === 'shipment.estimated_arrival_change') { + const shipment = event.data; + console.log(`Shipment ${shipment.attributes.bill_of_lading_number} ETA updated to: ${shipment.attributes.estimated_port_arrival_date}`); + + // Update your database or notify stakeholders + // ... + } + + // Always respond with 200 OK to acknowledge receipt + res.status(200).send('Event received'); +}); + +app.listen(3000, () => { + console.log('Webhook server running on port 3000'); +}); +``` + +```php +// PHP webhook handler + +``` + + +### Example Webhook Event: ETA Change -View the "Code Generation" button to see sample code. +When a shipment's ETA changes, you'll receive a webhook event like this: -```json http +```json { - "method": "post", - "url": "https://api.terminal49.com/v2/webhooks", - "headers": { - "Content-Type": "application/vnd.api+json", - "Authorization": "Token YOUR_API_KEY" - }, - "body": "{\r\n \"data\": {\r\n \"type\": \"webhook\",\r\n \"attributes\": {\r\n \"url\": \"https:\/\/webhook.site\/\",\r\n \"active\": true,\r\n \"events\": [\r\n \"*\"\r\n ]\r\n }\r\n }\r\n}" + "type": "shipment.estimated_arrival_change", + "data": { + "id": "7a8f8d5e-4c7d-4f8c-9b2a-3c6d8e9f0a1b", + "type": "shipment", + "attributes": { + "bill_of_lading_number": "MAEU9736478", + "estimated_port_arrival_date": "2023-04-17T16:30:00Z", + "previous_estimated_port_arrival_date": "2023-04-15T14:00:00Z", + // ...other attributes + } + } } -``` \ No newline at end of file +``` + + + **Testing Webhooks**: Use services like [webhook.site](https://webhook.site) or [ngrok](https://ngrok.com) during development to easily receive and inspect webhook events. + + +## Next Steps + +Now that you've set up basic tracking and notifications, you can: + +1. **Explore Use Cases**: + - [Container Availability Monitoring](/api-docs/in-depth-guides/use-cases#container-availability) + - [Terminal Hold Detection](/api-docs/in-depth-guides/use-cases#terminal-holds) + - [Last Free Day Tracking](/api-docs/in-depth-guides/use-cases#last-free-day) + +2. **Optimize Your Integration**: + - [Batch tracking multiple containers](/api-docs/in-depth-guides/json-api-guide#creating-resources) + - [Learn more about rate limits](/api-docs/in-depth-guides/rate-limiting) + - [Handle errors properly](/api-docs/in-depth-guides/error-handling) + +3. **Dive Deeper**: + - [Explore the full JSON:API implementation](/api-docs/in-depth-guides/json-api-guide) + - [Check out our Postman collection](https://app.getpostman.com/run-collection/4989da1ade6756b2f636) diff --git a/docs/api-docs/in-depth-guides/rate-limiting.mdx b/docs/api-docs/in-depth-guides/rate-limiting.mdx new file mode 100644 index 00000000..07162c6e --- /dev/null +++ b/docs/api-docs/in-depth-guides/rate-limiting.mdx @@ -0,0 +1,335 @@ +--- +title: Rate Limiting +description: Understanding Terminal49's API rate limits and best practices for efficient API usage +icon: "gauge-high" +--- + +# Rate Limiting + +This guide explains Terminal49's API rate limits and provides strategies for working efficiently within these limits. + +## Understanding Rate Limits + +Rate limiting is a technique used to control the amount of incoming and outgoing traffic to or from a network or service. Terminal49 implements rate limiting to ensure fair usage of the API across all users and to maintain overall system stability and performance. + +### Current Rate Limits + +Terminal49's API uses the following rate limiting structure: + +| Plan | Requests per minute | Requests per hour | Requests per day | +|------|---------------------|-------------------|------------------| +| Standard | 60 | 1,000 | 10,000 | +| Enterprise | 120 | 5,000 | 50,000 | + + + These limits may be adjusted based on your specific needs. If you're consistently hitting rate limits, please contact our support team to discuss options for increasing your quota. + + +## How Rate Limiting Works + +### Request Counting + +Each API request made with your API key counts toward your rate limit. The following requests all count as separate requests: + +- `GET /shipments` +- `GET /containers` +- `POST /tracking_requests` + +### Rate Limit Headers + +Terminal49 returns the following headers with each API response to help you monitor your rate limit usage: + +``` +X-RateLimit-Limit: 60 +X-RateLimit-Remaining: 58 +X-RateLimit-Reset: 1617192000 +``` + + + The maximum number of requests allowed in the current time window. + + + + The number of requests remaining in the current time window. + + + + The time at which the current rate limit window resets, in Unix time. + + +### Rate Limit Exceeded Response + +When you exceed the rate limit, the API will return a `429 Too Many Requests` status code with the following response body: + +```json +{ + "errors": [ + { + "status": "429", + "title": "Too Many Requests", + "detail": "Rate limit exceeded. Please wait and try again later." + } + ] +} +``` + +The response will also include headers indicating when you can retry your request. + +## Best Practices for Handling Rate Limits + +### Implement Retries with Exponential Backoff + +When you receive a 429 response, implement a retry mechanism with exponential backoff: + +```javascript +async function makeRequestWithRetry(endpoint, maxRetries = 3) { + let retries = 0; + + while (retries < maxRetries) { + try { + const response = await fetch(`https://api.terminal49.com/v2/${endpoint}`, { + headers: { + 'Authorization': `Token ${API_KEY}`, + 'Content-Type': 'application/vnd.api+json' + } + }); + + if (response.status === 429) { + // Parse the Retry-After header if available + const retryAfter = response.headers.get('Retry-After'); + const waitTime = retryAfter ? parseInt(retryAfter) * 1000 : Math.pow(2, retries) * 1000; + + console.log(`Rate limit exceeded. Retrying after ${waitTime}ms...`); + await new Promise(resolve => setTimeout(resolve, waitTime)); + retries++; + continue; + } + + return response; + } catch (error) { + console.error('API request failed:', error); + retries++; + + if (retries >= maxRetries) { + throw error; + } + + // Exponential backoff + const waitTime = Math.pow(2, retries) * 1000; + await new Promise(resolve => setTimeout(resolve, waitTime)); + } + } + + throw new Error('Maximum retry attempts reached'); +} +``` + +### Batch Requests When Possible + +Instead of making individual requests for each resource, use the `include` parameter to fetch related resources in a single request: + +``` +// Instead of: +GET /shipments/123 +GET /containers/456 +GET /containers/789 + +// Use: +GET /shipments/123?include=containers +``` + +This approach not only reduces the number of requests counting toward your rate limit but also improves overall performance. + +### Implement Request Queuing + +For applications that need to make many API requests, implement a request queue that respects the rate limits: + +```javascript +class RequestQueue { + constructor(requestsPerMinute = 60) { + this.queue = []; + this.processing = false; + this.interval = 60 * 1000 / requestsPerMinute; // Distribute requests evenly + } + + async add(endpoint, method = 'GET', data = null) { + return new Promise((resolve, reject) => { + this.queue.push({ endpoint, method, data, resolve, reject }); + if (!this.processing) { + this.processQueue(); + } + }); + } + + async processQueue() { + this.processing = true; + + while (this.queue.length > 0) { + const { endpoint, method, data, resolve, reject } = this.queue.shift(); + + try { + const result = await this.makeApiRequest(endpoint, method, data); + resolve(result); + } catch (error) { + reject(error); + } + + // Wait before processing the next request + await new Promise(resolve => setTimeout(resolve, this.interval)); + } + + this.processing = false; + } + + // Your API request implementation here + async makeApiRequest(endpoint, method, data) { + // ... + } +} + +// Usage: +const queue = new RequestQueue(60); // 60 requests per minute +const shipments = await queue.add('shipments'); +const containers = await queue.add('containers'); +``` + +### Cache Responses + +Implement caching for responses that don't change frequently to reduce the number of API calls: + +```javascript +const cache = new Map(); + +async function fetchWithCache(endpoint, cacheTime = 5 * 60 * 1000) { // 5 minutes + const cacheKey = endpoint; + const cachedData = cache.get(cacheKey); + + if (cachedData && cachedData.timestamp > Date.now() - cacheTime) { + console.log(`Using cached data for ${endpoint}`); + return cachedData.data; + } + + const response = await fetch(`https://api.terminal49.com/v2/${endpoint}`, { + headers: { + 'Authorization': `Token ${API_KEY}`, + 'Content-Type': 'application/vnd.api+json' + } + }); + + const data = await response.json(); + + cache.set(cacheKey, { + timestamp: Date.now(), + data + }); + + return data; +} +``` + +### Monitor Your Usage + +Implement monitoring to track your API usage and detect when you're approaching rate limits: + +```javascript +function monitorRateLimits(response) { + const limit = parseInt(response.headers.get('X-RateLimit-Limit')); + const remaining = parseInt(response.headers.get('X-RateLimit-Remaining')); + const resetTime = new Date(parseInt(response.headers.get('X-RateLimit-Reset')) * 1000); + + const usagePercentage = ((limit - remaining) / limit) * 100; + + console.log(`API Rate Limit: ${remaining}/${limit} remaining (${usagePercentage.toFixed(2)}%)`); + console.log(`Resets at: ${resetTime.toLocaleTimeString()}`); + + // Alert if usage is high + if (usagePercentage > 80) { + console.warn('API rate limit usage is high! Consider reducing request frequency.'); + } +} +``` + +## Enterprise Considerations + +### Webhooks as an Alternative + +For high-frequency updates to resources, consider using [webhooks](/api-docs/in-depth-guides/webhooks) instead of polling the API: + +```javascript +// Instead of polling every few minutes: +setInterval(async () => { + const shipments = await fetchWithCache('shipments?filter[status]=active'); + // Process shipments... +}, 5 * 60 * 1000); + +// Set up a webhook endpoint to receive updates in real-time +app.post('/webhook/shipment-updates', (req, res) => { + const shipmentEvent = req.body; + // Process shipment update in real-time + res.status(200).send('Event received'); +}); +``` + +### Request Rate Allocation + +If you're building an application with multiple components or users that share the same API key, consider implementing a token bucket algorithm to fairly distribute your rate limit quota: + +```javascript +class TokenBucket { + constructor(capacity, fillPerSecond) { + this.capacity = capacity; + this.tokens = capacity; + this.lastFill = Date.now(); + this.fillPerSecond = fillPerSecond; + } + + take(count = 1) { + this.refill(); + + if (this.tokens >= count) { + this.tokens -= count; + return true; + } + + return false; + } + + refill() { + const now = Date.now(); + const deltaSeconds = (now - this.lastFill) / 1000; + const newTokens = deltaSeconds * this.fillPerSecond; + + this.tokens = Math.min(this.capacity, this.tokens + newTokens); + this.lastFill = now; + } +} + +// Usage: +// For 60 requests per minute (1 per second) +const bucket = new TokenBucket(60, 1); + +async function makeRateLimitedRequest(endpoint) { + if (bucket.take()) { + return await fetch(`https://api.terminal49.com/v2/${endpoint}`); + } else { + // Wait and retry + await new Promise(resolve => setTimeout(resolve, 1000)); + return makeRateLimitedRequest(endpoint); + } +} +``` + +## Getting Help + +If you consistently hit rate limits despite implementing these best practices, you may need a higher quota for your use case. Contact Terminal49 support at [support@terminal49.com](mailto:support@terminal49.com) with: + +1. Your API key ID (not the actual key) +2. The endpoints that are hitting rate limits +3. Your typical request patterns and frequency +4. Business justification for needing a higher limit + +## Next Steps + +- Learn about [Error Handling](/api-docs/in-depth-guides/error-handling) for dealing with rate limit errors +- Explore [Webhooks](/api-docs/in-depth-guides/webhooks) as an alternative to frequent API polling +- Understand [Pagination](/api-docs/api-reference/pagination) to efficiently retrieve large datasets diff --git a/docs/api-docs/in-depth-guides/use-cases.mdx b/docs/api-docs/in-depth-guides/use-cases.mdx new file mode 100644 index 00000000..251bb5b4 --- /dev/null +++ b/docs/api-docs/in-depth-guides/use-cases.mdx @@ -0,0 +1,996 @@ +--- +title: Use Cases +description: Learn how to implement the most common logistics use cases with Terminal49's API, including container tracking, ETA monitoring, and hold management. +icon: "arrows-split-up-and-left" +--- + +# Use Cases + +This guide demonstrates the most common logistics use cases you can implement with Terminal49's API. For each use case, we'll show two approaches: + +1. **Request-based approach**: Proactively querying the API +2. **Event-driven approach**: Receiving webhook notifications (recommended) + + + The event-driven webhook approach is recommended for production environments as it is more efficient and ensures you get real-time updates. + + +## Getting Container ETAs + +One of the most common needs is knowing when containers will arrive at their destination port. + +### Request-Based Approach + +You can query the shipment data to get the current ETA: + + +```bash +# Using cURL +curl -X GET "https://api.terminal49.com/v2/shipments/7a8f8d5e-4c7d-4f8c-9b2a-3c6d8e9f0a1b" \ + -H "Authorization: Token YOUR_API_KEY" \ + -H "Content-Type: application/vnd.api+json" +``` + +```javascript +// Using JavaScript +const shipmentId = '7a8f8d5e-4c7d-4f8c-9b2a-3c6d8e9f0a1b'; +const response = await fetch(`https://api.terminal49.com/v2/shipments/${shipmentId}`, { + headers: { + 'Authorization': `Token ${YOUR_API_KEY}`, + 'Content-Type': 'application/vnd.api+json' + } +}); + +const result = await response.json(); +const eta = result.data.attributes.estimated_port_arrival_date; +console.log(`Container will arrive on: ${new Date(eta).toLocaleString()}`); +``` + +```ruby +# Using Ruby +require 'httparty' + +shipment_id = '7a8f8d5e-4c7d-4f8c-9b2a-3c6d8e9f0a1b' +headers = { + 'Authorization' => "Token YOUR_API_KEY", + 'Content-Type' => 'application/vnd.api+json' +} + +response = HTTParty.get( + "https://api.terminal49.com/v2/shipments/#{shipment_id}", + headers: headers +) + +data = JSON.parse(response.body) +eta = data['data']['attributes']['estimated_port_arrival_date'] +puts "Container will arrive on: #{eta}" +``` + + +### Event-Driven Approach (Recommended) + +Register a webhook to be notified when ETAs change: + +1. Register a webhook for ETA change events: + + +```bash +# Using cURL +curl -X POST "https://api.terminal49.com/v2/webhooks" \ + -H "Authorization: Token YOUR_API_KEY" \ + -H "Content-Type: application/vnd.api+json" \ + -d '{ + "data": { + "type": "webhook", + "attributes": { + "url": "https://your-server.com/webhook", + "active": true, + "events": ["shipment.estimated_arrival_change"] + } + } + }' +``` + +```javascript +// Using JavaScript +const response = await fetch('https://api.terminal49.com/v2/webhooks', { + method: 'POST', + headers: { + 'Authorization': `Token ${YOUR_API_KEY}`, + 'Content-Type': 'application/vnd.api+json' + }, + body: JSON.stringify({ + data: { + type: 'webhook', + attributes: { + url: 'https://your-server.com/webhook', + active: true, + events: ['shipment.estimated_arrival_change'] + } + } + }) +}); +``` + + +2. Implement a webhook handler to process ETA change notifications: + +```javascript +// Node.js with Express example +app.post('/webhook', (req, res) => { + const event = req.body; + + if (event.type === 'shipment.estimated_arrival_change') { + const shipment = event.data; + const bol = shipment.attributes.bill_of_lading_number; + const newEta = new Date(shipment.attributes.estimated_port_arrival_date); + const previousEta = new Date(shipment.attributes.previous_estimated_port_arrival_date); + + // Calculate the difference in days + const diffDays = Math.round((newEta - previousEta) / (1000 * 60 * 60 * 24)); + const direction = diffDays >= 0 ? 'delayed' : 'advanced'; + + console.log(`ETA Update: Shipment ${bol} has been ${direction} by ${Math.abs(diffDays)} days.`); + console.log(`New ETA: ${newEta.toLocaleString()}`); + + // Here you could: + // 1. Update your database + // 2. Notify stakeholders via email/SMS + // 3. Update planning systems + + // Example email notification + sendEmail({ + to: 'logistics@yourcompany.com', + subject: `ETA Update: Shipment ${bol} ${direction} by ${Math.abs(diffDays)} days`, + body: `The estimated arrival date for shipment ${bol} has changed from ${previousEta.toLocaleString()} to ${newEta.toLocaleString()}.` + }); + } + + res.status(200).send('Event received'); +}); +``` + + +## Container Availability Monitoring + +Knowing when a container is available for pickup is critical for drayage planning and avoiding detention fees. + +### Request-Based Approach + +Poll the container endpoint to check availability status: + + +```bash +# Using cURL +curl -X GET "https://api.terminal49.com/v2/containers/e5g4h8i2-4f8c-9b2a-3c6d8e9f0a1b" \ + -H "Authorization: Token YOUR_API_KEY" \ + -H "Content-Type: application/vnd.api+json" +``` + +```javascript +// Using JavaScript +const containerId = 'e5g4h8i2-4f8c-9b2a-3c6d8e9f0a1b'; +const response = await fetch(`https://api.terminal49.com/v2/containers/${containerId}`, { + headers: { + 'Authorization': `Token ${YOUR_API_KEY}`, + 'Content-Type': 'application/vnd.api+json' + } +}); + +const result = await response.json(); +const container = result.data; + +if (container.attributes.available_for_pickup) { + console.log(`Container ${container.attributes.number} is available for pickup!`); + console.log(`Last free day: ${container.attributes.last_free_day}`); +} else { + console.log(`Container ${container.attributes.number} is not yet available.`); + console.log(`Current status: ${container.attributes.status}`); +} +``` + + +### Event-Driven Approach (Recommended) + +Register a webhook to receive notifications when container availability changes: + +1. Register a webhook for container availability events: + + +```bash +# Using cURL +curl -X POST "https://api.terminal49.com/v2/webhooks" \ + -H "Authorization: Token YOUR_API_KEY" \ + -H "Content-Type: application/vnd.api+json" \ + -d '{ + "data": { + "type": "webhook", + "attributes": { + "url": "https://your-server.com/webhook", + "active": true, + "events": ["container.available_for_pickup", "container.availability_changed"] + } + } + }' +``` + +```javascript +// Using JavaScript +const response = await fetch('https://api.terminal49.com/v2/webhooks', { + method: 'POST', + headers: { + 'Authorization': `Token ${YOUR_API_KEY}`, + 'Content-Type': 'application/vnd.api+json' + }, + body: JSON.stringify({ + data: { + type: 'webhook', + attributes: { + url: 'https://your-server.com/webhook', + active: true, + events: ['container.available_for_pickup', 'container.availability_changed'] + } + } + }) +}); +``` + + +2. Implement a webhook handler for availability notifications: + +```javascript +// Node.js handler +app.post('/webhook', (req, res) => { + const event = req.body; + + if (event.type === 'container.available_for_pickup') { + const container = event.data; + const number = container.attributes.number; + const lastFreeDay = new Date(container.attributes.last_free_day); + const daysUntilFee = Math.ceil((lastFreeDay - new Date()) / (1000 * 60 * 60 * 24)); + + console.log(`Container ${number} is now available for pickup!`); + console.log(`Last free day: ${lastFreeDay.toLocaleDateString()}`); + console.log(`Days until storage fees: ${daysUntilFee}`); + + // Notify drayage team + sendNotification({ + team: 'drayage', + priority: 'high', + message: `Container ${number} is ready for pickup. Last free day: ${lastFreeDay.toLocaleDateString()}` + }); + + // Add to pickup schedule + addToPickupSchedule({ + containerNumber: number, + availableFrom: new Date(), + lastFreeDay: lastFreeDay, + terminal: container.attributes.terminal_name, + size: container.attributes.size_type + }); + } + + else if (event.type === 'container.availability_changed') { + const container = event.data; + + if (!container.attributes.available_for_pickup) { + console.log(`Container ${container.attributes.number} is no longer available for pickup!`); + console.log(`Current status: ${container.attributes.status}`); + + // Remove from pickup schedule or mark as unavailable + updatePickupSchedule({ + containerNumber: container.attributes.number, + available: false, + reason: container.attributes.status + }); + } + } + + res.status(200).send('Event received'); +}); +``` + + +## Terminal Hold Detection + +Detecting and addressing holds early can prevent delays in container pickup. + +### Request-Based Approach + +Query the container's holds to check for any active holds: + + +```bash +# Using cURL +curl -X GET "https://api.terminal49.com/v2/containers/e5g4h8i2-4f8c-9b2a-3c6d8e9f0a1b?include=holds" \ + -H "Authorization: Token YOUR_API_KEY" \ + -H "Content-Type: application/vnd.api+json" +``` + +```javascript +// Using JavaScript +const containerId = 'e5g4h8i2-4f8c-9b2a-3c6d8e9f0a1b'; +const response = await fetch(`https://api.terminal49.com/v2/containers/${containerId}?include=holds`, { + headers: { + 'Authorization': `Token ${YOUR_API_KEY}`, + 'Content-Type': 'application/vnd.api+json' + } +}); + +const result = await response.json(); +const container = result.data; + +// Check if the container has holds +const hasHolds = container.relationships.holds && + container.relationships.holds.data && + container.relationships.holds.data.length > 0; + +if (hasHolds) { + console.log(`Container ${container.attributes.number} has holds!`); + + // Extract hold details from included data + const holds = result.included.filter(item => item.type === 'hold'); + + holds.forEach(hold => { + console.log(`Hold type: ${hold.attributes.type}`); + console.log(`Hold reason: ${hold.attributes.reason}`); + console.log(`Applied at: ${new Date(hold.attributes.created_at).toLocaleString()}`); + }); +} else { + console.log(`Container ${container.attributes.number} has no holds.`); +} +``` + + +### Event-Driven Approach (Recommended) + +Set up a webhook to be notified when a hold is applied or removed: + +1. Register a webhook for hold events: + + +```bash +# Using cURL +curl -X POST "https://api.terminal49.com/v2/webhooks" \ + -H "Authorization: Token YOUR_API_KEY" \ + -H "Content-Type: application/vnd.api+json" \ + -d '{ + "data": { + "type": "webhook", + "attributes": { + "url": "https://your-server.com/webhook", + "active": true, + "events": ["container.hold.created", "container.hold.removed"] + } + } + }' +``` + +```javascript +// Using JavaScript +const response = await fetch('https://api.terminal49.com/v2/webhooks', { + method: 'POST', + headers: { + 'Authorization': `Token ${YOUR_API_KEY}`, + 'Content-Type': 'application/vnd.api+json' + }, + body: JSON.stringify({ + data: { + type: 'webhook', + attributes: { + url: 'https://your-server.com/webhook', + active: true, + events: ['container.hold.created', 'container.hold.removed'] + } + } + }) +}); +``` + + +2. Implement a webhook handler for hold notifications: + +```javascript +// Node.js handler +app.post('/webhook', (req, res) => { + const event = req.body; + + if (event.type === 'container.hold.created') { + const hold = event.data; + const container = event.included.find(item => + item.type === 'container' && item.id === hold.relationships.container.data.id + ); + + const containerNumber = container.attributes.number; + const holdType = hold.attributes.type; + const holdReason = hold.attributes.reason; + + console.log(`New hold detected on container ${containerNumber}!`); + console.log(`Hold type: ${holdType}`); + console.log(`Hold reason: ${holdReason}`); + + // Different actions based on hold type + if (holdType === 'customs') { + // Notify customs broker + notifyCustomsBroker({ + containerNumber, + holdType, + holdReason, + priority: 'urgent' + }); + } else if (holdType === 'freight') { + // Notify accounting department + notifyAccounting({ + containerNumber, + holdType, + holdReason, + message: 'Payment required to release container' + }); + } else { + // Generic notification + notifyLogisticsTeam({ + containerNumber, + holdType, + holdReason + }); + } + } + + else if (event.type === 'container.hold.removed') { + const hold = event.data; + const container = event.included.find(item => + item.type === 'container' && item.id === hold.relationships.container.data.id + ); + + const containerNumber = container.attributes.number; + const holdType = hold.attributes.type; + + console.log(`Hold removed from container ${containerNumber}!`); + console.log(`Hold type: ${holdType} has been cleared`); + + // Notify relevant teams + notifyTeams({ + containerNumber, + message: `The ${holdType} hold has been removed. Container can now proceed.`, + priority: 'high' + }); + + // Check if container is now available for pickup + if (container.attributes.available_for_pickup) { + addToPickupSchedule({ + containerNumber, + availableFrom: new Date(), + terminal: container.attributes.terminal_name + }); + } + } + + res.status(200).send('Event received'); +}); +``` + + +## Last Free Day Tracking + +Tracking Last Free Day (LFD) is essential for avoiding storage fees. + +### Request-Based Approach + +Query containers and check their last free days: + + +```bash +# Using cURL +curl -X GET "https://api.terminal49.com/v2/containers?filter[status]=available_for_pickup" \ + -H "Authorization: Token YOUR_API_KEY" \ + -H "Content-Type: application/vnd.api+json" +``` + +```javascript +// Using JavaScript +const today = new Date(); +const threeDaysFromNow = new Date(today); +threeDaysFromNow.setDate(today.getDate() + 3); + +const response = await fetch('https://api.terminal49.com/v2/containers?filter[status]=available_for_pickup', { + headers: { + 'Authorization': `Token ${YOUR_API_KEY}`, + 'Content-Type': 'application/vnd.api+json' + } +}); + +const result = await response.json(); +const containers = result.data; + +// Find containers with upcoming last free days +const upcomingLFDs = containers.filter(container => { + const lastFreeDay = new Date(container.attributes.last_free_day); + return lastFreeDay > today && lastFreeDay <= threeDaysFromNow; +}); + +if (upcomingLFDs.length > 0) { + console.log(`Found ${upcomingLFDs.length} containers with LFD in the next 3 days:`); + + upcomingLFDs.forEach(container => { + const lfd = new Date(container.attributes.last_free_day); + const daysRemaining = Math.ceil((lfd - today) / (1000 * 60 * 60 * 24)); + + console.log(`Container ${container.attributes.number}: LFD in ${daysRemaining} days (${lfd.toLocaleDateString()})`); + console.log(`Terminal: ${container.attributes.terminal_name}`); + console.log(`Size: ${container.attributes.size_type}`); + console.log('---'); + }); + + // Prioritize containers with the earliest LFDs + upcomingLFDs.sort((a, b) => + new Date(a.attributes.last_free_day) - new Date(b.attributes.last_free_day) + ); + + // Create pickup schedule based on LFD + createPickupSchedule(upcomingLFDs); +} +``` + + +### Event-Driven Approach (Recommended) + +Set up a webhook for last free day changes and implement an automated daily check: + +1. Register a webhook for last free day updates: + + +```bash +# Using cURL +curl -X POST "https://api.terminal49.com/v2/webhooks" \ + -H "Authorization: Token YOUR_API_KEY" \ + -H "Content-Type: application/vnd.api+json" \ + -d '{ + "data": { + "type": "webhook", + "attributes": { + "url": "https://your-server.com/webhook", + "active": true, + "events": ["container.last_free_day_change"] + } + } + }' +``` + +```javascript +// Using JavaScript +const response = await fetch('https://api.terminal49.com/v2/webhooks', { + method: 'POST', + headers: { + 'Authorization': `Token ${YOUR_API_KEY}`, + 'Content-Type': 'application/vnd.api+json' + }, + body: JSON.stringify({ + data: { + type: 'webhook', + attributes: { + url: 'https://your-server.com/webhook', + active: true, + events: ['container.last_free_day_change'] + } + } + }) +}); +``` + + +2. Implement a webhook handler for LFD changes and daily checks: + +```javascript +// Node.js handler for webhook events +app.post('/webhook', (req, res) => { + const event = req.body; + + if (event.type === 'container.last_free_day_change') { + const container = event.data; + const newLFD = new Date(container.attributes.last_free_day); + const previousLFD = container.attributes.previous_last_free_day ? + new Date(container.attributes.previous_last_free_day) : null; + + console.log(`Last Free Day updated for container ${container.attributes.number}`); + + if (previousLFD) { + const diffDays = Math.round((newLFD - previousLFD) / (1000 * 60 * 60 * 24)); + const direction = diffDays >= 0 ? 'extended' : 'reduced'; + + console.log(`LFD has been ${direction} by ${Math.abs(diffDays)} days`); + console.log(`Old LFD: ${previousLFD.toLocaleDateString()}`); + console.log(`New LFD: ${newLFD.toLocaleDateString()}`); + } else { + console.log(`LFD set to ${newLFD.toLocaleDateString()}`); + } + + // Update pickup schedule + updatePickupPriority({ + containerNumber: container.attributes.number, + lastFreeDay: newLFD, + terminal: container.attributes.terminal_name + }); + + // Calculate days remaining + const today = new Date(); + const daysRemaining = Math.ceil((newLFD - today) / (1000 * 60 * 60 * 24)); + + // Send notifications based on urgency + if (daysRemaining <= 1) { + // URGENT: Last free day is today or tomorrow + sendUrgentNotification({ + containerNumber: container.attributes.number, + message: `URGENT: Last free day is ${daysRemaining === 0 ? 'TODAY' : 'TOMORROW'}`, + terminal: container.attributes.terminal_name + }); + } else if (daysRemaining <= 3) { + // High priority: Last free day within 3 days + sendPriorityNotification({ + containerNumber: container.attributes.number, + message: `Last free day in ${daysRemaining} days`, + terminal: container.attributes.terminal_name + }); + } + } + + res.status(200).send('Event received'); +}); + +// Daily scheduled check function (run via cron job) +async function checkUpcomingLastFreeDays() { + const today = new Date(); + + try { + const response = await fetch('https://api.terminal49.com/v2/containers?filter[status]=available_for_pickup', { + headers: { + 'Authorization': `Token ${process.env.API_KEY}`, + 'Content-Type': 'application/vnd.api+json' + } + }); + + const result = await response.json(); + const containers = result.data; + + // Group containers by days until last free day + const lfdGroups = { + today: [], + tomorrow: [], + twoDays: [], + threeDays: [], + fourToSeven: [], + overOneWeek: [] + }; + + containers.forEach(container => { + if (!container.attributes.last_free_day) return; + + const lfd = new Date(container.attributes.last_free_day); + const daysRemaining = Math.ceil((lfd - today) / (1000 * 60 * 60 * 24)); + + if (daysRemaining <= 0) { + lfdGroups.today.push(container); + } else if (daysRemaining === 1) { + lfdGroups.tomorrow.push(container); + } else if (daysRemaining === 2) { + lfdGroups.twoDays.push(container); + } else if (daysRemaining === 3) { + lfdGroups.threeDays.push(container); + } else if (daysRemaining <= 7) { + lfdGroups.fourToSeven.push(container); + } else { + lfdGroups.overOneWeek.push(container); + } + }); + + // Send daily summary report + sendDailyLFDReport({ + urgent: [...lfdGroups.today, ...lfdGroups.tomorrow], + priority: [...lfdGroups.twoDays, ...lfdGroups.threeDays], + upcoming: lfdGroups.fourToSeven, + later: lfdGroups.overOneWeek + }); + + // Take immediate action for urgent containers + if (lfdGroups.today.length > 0) { + lfdGroups.today.forEach(container => { + sendUrgentPickupRequest({ + containerNumber: container.attributes.number, + terminal: container.attributes.terminal_name, + message: 'URGENT: Last free day is TODAY. Immediate pickup required.' + }); + }); + } + } catch (error) { + console.error('Error checking last free days:', error); + } +} +``` + +## Vessel Arrival Monitoring + +Tracking vessel arrivals helps with resource planning and customer updates. + +### Request-Based Approach + +Query the shipment for its vessel and voyage information: + + +```bash +# Using cURL +curl -X GET "https://api.terminal49.com/v2/shipments/7a8f8d5e-4c7d-4f8c-9b2a-3c6d8e9f0a1b" \ + -H "Authorization: Token YOUR_API_KEY" \ + -H "Content-Type: application/vnd.api+json" +``` + +```javascript +// Using JavaScript +const shipmentId = '7a8f8d5e-4c7d-4f8c-9b2a-3c6d8e9f0a1b'; +const response = await fetch(`https://api.terminal49.com/v2/shipments/${shipmentId}`, { + headers: { + 'Authorization': `Token ${YOUR_API_KEY}`, + 'Content-Type': 'application/vnd.api+json' + } +}); + +const result = await response.json(); +const shipment = result.data; + +console.log(`Vessel: ${shipment.attributes.vessel_name}`); +console.log(`Voyage: ${shipment.attributes.voyage}`); +console.log(`Estimated arrival: ${new Date(shipment.attributes.estimated_port_arrival_date).toLocaleString()}`); +console.log(`Port of discharge: ${shipment.attributes.port_of_discharge_name}`); + +// Calculate days until arrival +const today = new Date(); +const arrivalDate = new Date(shipment.attributes.estimated_port_arrival_date); +const daysUntilArrival = Math.ceil((arrivalDate - today) / (1000 * 60 * 60 * 24)); + +console.log(`Days until arrival: ${daysUntilArrival}`); +``` + + +### Event-Driven Approach (Recommended) + +Set up webhooks to be notified of vessel arrival and changes: + +1. Register a webhook for vessel events: + + +```bash +# Using cURL +curl -X POST "https://api.terminal49.com/v2/webhooks" \ + -H "Authorization: Token YOUR_API_KEY" \ + -H "Content-Type: application/vnd.api+json" \ + -d '{ + "data": { + "type": "webhook", + "attributes": { + "url": "https://your-server.com/webhook", + "active": true, + "events": [ + "shipment.vessel_arrived", + "shipment.vessel_name_change", + "shipment.estimated_arrival_change" + ] + } + } + }' +``` + +```javascript +// Using JavaScript +const response = await fetch('https://api.terminal49.com/v2/webhooks', { + method: 'POST', + headers: { + 'Authorization': `Token ${YOUR_API_KEY}`, + 'Content-Type': 'application/vnd.api+json' + }, + body: JSON.stringify({ + data: { + type: 'webhook', + attributes: { + url: 'https://your-server.com/webhook', + active: true, + events: [ + 'shipment.vessel_arrived', + 'shipment.vessel_name_change', + 'shipment.estimated_arrival_change' + ] + } + } + }) +}); +``` + + +2. Implement a webhook handler for vessel events: + +```javascript +// Node.js handler +app.post('/webhook', (req, res) => { + const event = req.body; + const shipment = event.data; + + if (event.type === 'shipment.vessel_arrived') { + console.log(`Vessel ${shipment.attributes.vessel_name} has arrived at ${shipment.attributes.port_of_discharge_name}!`); + + // Notify stakeholders of vessel arrival + notifyCustomer({ + shipmentId: shipment.id, + bolNumber: shipment.attributes.bill_of_lading_number, + message: `Your shipment has arrived at ${shipment.attributes.port_of_discharge_name} on vessel ${shipment.attributes.vessel_name}.`, + estimatedAvailability: 'Typically 2-3 days after vessel arrival' + }); + + // Begin monitoring container availability + startAvailabilityMonitoring(shipment.id); + } + + else if (event.type === 'shipment.vessel_name_change') { + console.log(`Vessel change for shipment ${shipment.attributes.bill_of_lading_number}!`); + console.log(`New vessel: ${shipment.attributes.vessel_name}`); + console.log(`New voyage: ${shipment.attributes.voyage}`); + + // Notify operations team + notifyOperations({ + shipmentId: shipment.id, + bolNumber: shipment.attributes.bill_of_lading_number, + message: `Vessel change: Now on ${shipment.attributes.vessel_name} voyage ${shipment.attributes.voyage}`, + newETA: shipment.attributes.estimated_port_arrival_date + }); + } + + else if (event.type === 'shipment.estimated_arrival_change') { + const newArrival = new Date(shipment.attributes.estimated_port_arrival_date); + const previousArrival = shipment.attributes.previous_estimated_port_arrival_date ? + new Date(shipment.attributes.previous_estimated_port_arrival_date) : null; + + if (previousArrival) { + const diffDays = Math.round((newArrival - previousArrival) / (1000 * 60 * 60 * 24)); + const direction = diffDays >= 0 ? 'delayed' : 'advanced'; + + console.log(`Vessel ${shipment.attributes.vessel_name} has been ${direction} by ${Math.abs(diffDays)} days`); + console.log(`New ETA: ${newArrival.toLocaleDateString()}`); + + // Update pick-up planning based on new ETA + updatePickupPlanning({ + shipmentId: shipment.id, + bolNumber: shipment.attributes.bill_of_lading_number, + newETA: newArrival, + diffDays + }); + + // Notify customer of significant changes (more than 1 day) + if (Math.abs(diffDays) > 1) { + notifyCustomer({ + shipmentId: shipment.id, + bolNumber: shipment.attributes.bill_of_lading_number, + message: `Your shipment on ${shipment.attributes.vessel_name} has been ${direction} by ${Math.abs(diffDays)} days. New estimated arrival: ${newArrival.toLocaleDateString()}`, + previousETA: previousArrival.toLocaleDateString(), + newETA: newArrival.toLocaleDateString() + }); + } + } + } + + res.status(200).send('Event received'); +}); +``` + +## Using Webhooks for Multiple Workflows + +For production environments, you'll likely want to track multiple event types with a single webhook. Here's how to set it up: + +1. Register a webhook for all relevant event types: + +```javascript +// Using JavaScript +const response = await fetch('https://api.terminal49.com/v2/webhooks', { + method: 'POST', + headers: { + 'Authorization': `Token ${YOUR_API_KEY}`, + 'Content-Type': 'application/vnd.api+json' + }, + body: JSON.stringify({ + data: { + type: 'webhook', + attributes: { + url: 'https://your-server.com/webhook', + active: true, + events: [ + // Shipment events + 'shipment.estimated_arrival_change', + 'shipment.vessel_arrived', + 'shipment.vessel_name_change', + + // Container events + 'container.available_for_pickup', + 'container.availability_changed', + 'container.last_free_day_change', + 'container.hold.created', + 'container.hold.removed', + + // Tracking request events + 'tracking_request.succeeded', + 'tracking_request.failed' + ] + } + } + }) +}); +``` + +2. Implement a comprehensive webhook handler: + +```javascript +// Node.js handler with comprehensive event routing +app.post('/webhook', (req, res) => { + const event = req.body; + console.log(`Received event: ${event.type}`); + + // Route the event to the appropriate handler based on event type + switch (event.type) { + // Shipment events + case 'shipment.estimated_arrival_change': + handleETAChange(event); + break; + case 'shipment.vessel_arrived': + handleVesselArrival(event); + break; + case 'shipment.vessel_name_change': + handleVesselChange(event); + break; + + // Container events + case 'container.available_for_pickup': + handleContainerAvailable(event); + break; + case 'container.availability_changed': + handleAvailabilityChange(event); + break; + case 'container.last_free_day_change': + handleLFDChange(event); + break; + case 'container.hold.created': + handleHoldCreated(event); + break; + case 'container.hold.removed': + handleHoldRemoved(event); + break; + + // Tracking request events + case 'tracking_request.succeeded': + handleTrackingSuccess(event); + break; + case 'tracking_request.failed': + handleTrackingFailure(event); + break; + + default: + console.log(`Unhandled event type: ${event.type}`); + } + + // Always acknowledge receipt of the webhook + res.status(200).send('Event received'); +}); + +// Individual handler functions (implementation details omitted for brevity) +function handleETAChange(event) { + // Implementation for ETA changes +} + +function handleVesselArrival(event) { + // Implementation for vessel arrivals +} + +// And so on for each event type... +``` + +## Next Steps + +Now that you understand the common workflows, you can: + +1. **Customize these examples** for your specific business needs +2. **Combine multiple workflows** to create comprehensive logistics applications +3. **Explore our API reference** for more details on available endpoints and parameters +4. **Check out our [JSON:API Guide](/api-docs/in-depth-guides/json-api-guide)** to learn how to efficiently request related resources diff --git a/docs/api-docs/in-depth-guides/webhooks.mdx b/docs/api-docs/in-depth-guides/webhooks.mdx index 621a6e52..1028c787 100644 --- a/docs/api-docs/in-depth-guides/webhooks.mdx +++ b/docs/api-docs/in-depth-guides/webhooks.mdx @@ -1,1443 +1,402 @@ --- -title: Webhooks +title: Implementing Webhooks +description: Learn how to set up webhooks to receive real-time updates from Terminal49 for your shipments and containers. --- -## Creating Webhooks -You may subscribe to events through webhooks to be alerted when events are triggered. +# Webhooks in Terminal49 -Visit https://app.terminal49.com/developers/webhooks and click the 'Create Webhook Endpoint' button to create your webhook through the UI. +Webhooks provide real-time notifications about changes to your shipments and containers, letting you build responsive applications that update as soon as new information becomes available. -If you prefer to create webhooks programatically then see the [webhooks post endpoint documentation](/api-docs/api-reference/webhooks/create-a-webhook). +## How Webhooks Work + + ![Terminal49 Webhook Flow](/images/webhook-flow.png) + -## Available Webook Events +1. **You register a webhook** with a URL endpoint that can receive POST requests +2. **An event occurs** on Terminal49 (e.g., a vessel arrives or customs status changes) +3. **Terminal49 sends a notification** to your registered URL with details about the event +4. **Your application processes** the notification and takes appropriate action -Each `WebhookNotification` event represents some change to a model which you may be notified of. +## Setting Up Webhooks -List of Supported Events: +### Step 1: Create a Webhook Endpoint -Event | Description ----------|---------- - `tracking_request.succeeded` | Shipment created and linked to `TrackingRequest` - `tracking_request.failed` | `TrackingRequest` failed and shipment was not created - `tracking_request.awaiting_manifest` | `TrackingRequest` awaiting a manifest - `tracking_request.tracking_stopped` | Terminal49 is no longer updating this `TrackingRequest`. - `container.transport.empty_out` | Empty out at port of lading - `container.transport.full_in` | Full in at port of lading - `container.transport.vessel_loaded` | Vessel loaded at port of lading - `container.transport.vessel_departed` | Vessel departed at port of lading - `container.transport.transshipment_arrived` | Container arrived at transhipment port - `container.transport.transshipment_discharged` | Container discharged at transhipment port - `container.transport.transshipment_loaded` | Container loaded at transhipment port - `container.transport.transshipment_departed` | Container departed at transhipment port - `container.transport.feeder_arrived` | Container arrived on feeder vessel or barge - `container.transport.feeder_discharged` | Container discharged from feeder vessel or barge - `container.transport.feeder_loaded` | Container loaded on feeder vessel or barge - `container.transport.feeder_departed` | Container departed on feeder vessel or barge - `container.transport.vessel_arrived` | Container arrived on vessel at port of discharge (destination port) - `container.transport.vessel_berthed` | Container on vessel berthed at port of discharge (destination port) - `container.transport.vessel_discharged` | Container discharged at port of discharge - `container.transport.full_out` | Full out at port of discharge - `container.transport.empty_in` | Empty returned at destination - `container.transport.rail_loaded` | Rail loaded - `container.transport.rail_departed` | Rail departed - `container.transport.rail_arrived` | Rail arrived - `container.transport.rail_unloaded` | Rail unloaded - `shipment.estimated.arrival` | ETA change notification (for port of discharge) - `container.created` | Container added to shipment. Helpful for seeing new containers on a booking or BL. - `container.updated` | Container attribute(s) updated (see below example) - `container.pod_terminal_changed` | Port of discharge assignment changed for container - `container.transport.arrived_at_inland_destination` | Container arrived at inland destination - `container.transport.estimated.arrived_at_inland_destination` | ETA change notification (for destination) - `container.pickup_lfd.changed` | Last Free Day (LFD) changed for container +First, you need to create an endpoint on your server that can receive POST requests. Here's an example using Node.js with Express: +```javascript title="Webhook Endpoint" +const express = require('express'); +const app = express(); +app.use(express.json()); +app.post('/terminal49-webhook', (req, res) => { + // Extract the event data + const webhookData = req.body; -## Receiving Webhooks + // Process the webhook (recommended to do this asynchronously) + processWebhook(webhookData); -When an event is triggered we will attempt to post to the URL you provided with the webhook. + // Always return a 200 status code quickly to acknowledge receipt + res.status(200).send('Webhook received'); +}); -The payload of every webhook is a `webhook_notification`. Each Webhook notification includes a `reference_object` in it's relationships which is the subject of that notification (e.g. a tracking request, or an updated container). +function processWebhook(webhookData) { + // Extract the event type + const event = webhookData.data.attributes.event; -Please note that we expect the endpoint to return [HTTP 200 OK](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/200), [HTTP 201](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/201), [HTTP 202](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/202) or [HTTP 204](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/204). We aim to deliver all webhook notifications, so any other response, including timeout, will result in a dozen of retries. + // Extract the reference object (what the event is about) + const referenceType = webhookData.data.relationships.reference_object.data.type; + const referenceId = webhookData.data.relationships.reference_object.data.id; -```json json_schema -{ - "type":"object", - "properties":{ - "data":{ - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid" - }, - "type": { - "type": "string", - "enum": [ - "webhook_notification" - ] - }, - "attributes": { - "type": "object", - "properties": { - "event": { - "type": "string" - }, - "delivery_status": { - "type": "string", - "default": "pending", - "enum": [ - "pending", - "succeeded", - "failed" - ], - "description": "Whether the notification has been delivered to the webhook endpoint" - }, - "created_at": { - "type": "string" - } - }, - "required": [ - "event", - "delivery_status", - "created_at" - ] - }, - "relationships": { - "type": "object", - "properties": { - "webhook": { - "type": "object", - "properties": { - "data": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid" - }, - "type": { - "type": "string", - "enum": [ - "webhook" - ] - } - } - } - } - }, - "reference_object": { - "type": "object", - "properties": { - "data": { - "type": "object", - "properties": { - "id": { - "type": "string", - "format": "uuid" - }, - "type": { - "type": "string", - "enum": [ - "tracking_request", - "estimated_event", - "transport_event", - "container_updated_event" - ] - } - } - } - } - } - }, - "required": [ - "webhook" - ] - } - } - }, - "included":{ - "type":"array", - "items": { - "anyOf": [ - { - "type": "object", - "title": "Webhook", - }, - { - "type": "object", - "title": "Tracking Request", - }, - { - "type": "object", - "title": "Transport Event", - }, - { - "type": "object", - "title": "Estimated Event", - }, - { - "type": "object", - "title": "Container Updated Event", - }, - { - "type": "object", - "title": "Terminal", - }, - { - "type": "object", - "title": "Port", - }, - - ] - } - } + console.log(`Processing ${event} for ${referenceType} ${referenceId}`); + // Handle different event types + switch (event) { + case 'container.transport.vessel_arrived': + handleVesselArrival(webhookData); + break; + case 'container.available_for_pickup': + handleContainerAvailable(webhookData); + break; + case 'container.pickup_lfd.changed': + handleLastFreeDayChanged(webhookData); + break; + // Add cases for other events you're interested in + default: + console.log(`Unhandled event type: ${event}`); } } -``` - -> [How to Troubleshoot Missing Webhook Notifications](https://help.terminal49.com/en/articles/7851422-missing-webhook-notifications) - - -## Security -There are a few ways you can verify the webhooks sent by Terminal49. - -Verify webhook signatures to confirm that received events are sent from Terminal49. Additionally, Terminal49 sends webhook events from a set list of IP addresses. Only trust events coming from these IP addresses. - - - -### Webhook notification origin IP -The full list of IP addresses that webhook notifications may come from is: - -``` -35.222.62.171 -3.230.67.145 -44.217.15.129 -``` - -### Verifying the webhook signature (optional) -When you create or get a webhook the model will include an attribute `secret`. - -Whenever a webhook notification is delivered we create a signature by using the webhook `secret` as the key to generate a HMAC hex digest with SHA-256 on the body. - -This signature is added as the header `X-T49-Webhook-Signature` - -If you would like to verify that the webhook payload has not been tampered with by a 3rd party, then you can perform the same operation on the response body with the webhook secret and confirm that the digests match. - -Below is a basic example of how this might look in a rails application. -```ruby -class WebhooksController < ApplicationController - def receive_tracking_request - secret = ENV.fetch('TRACKING_REQUEST_WEBHOOK_SECRET') - raise 'InvalidSignature' unless valid_signature?(request, secret) - - # continue processing webhook payload... - end - - private - - def valid_signature?(request, secret) - hmac = OpenSSL::HMAC.hexdigest('SHA256', secret, request.body.read) - request.headers['X-T49-Webhook-Signature'] == hmac - end -end +app.listen(3000, () => { + console.log('Webhook server running on port 3000'); +}); ``` + + Your webhook endpoint must be publicly accessible over the internet for Terminal49 to send notifications to it. During development, you can use tools like [ngrok](https://ngrok.com/) to create a temporary public URL. + + +### Step 2: Register Your Webhook with Terminal49 + +Once your endpoint is ready, register it with Terminal49's API: + +```javascript title="Register Webhook" +async function registerWebhook() { + try { + const response = await fetch('https://api.terminal49.com/v2/webhooks', { + method: 'POST', + headers: { + 'Authorization': 'Token YOUR_API_KEY', + 'Content-Type': 'application/vnd.api+json' + }, + body: JSON.stringify({ + data: { + type: "webhook", + attributes: { + url: "https://your-domain.com/terminal49-webhook", + event_types: [ + "container.transport.vessel_arrived", + "container.transport.vessel_discharged", + "container.available_for_pickup", + "container.pickup_lfd.changed", + "container.holds.changed", + "tracking_request.succeeded", + "tracking_request.failed" + ] + } + } + }) + }); + const result = await response.json(); + console.log('Webhook registered:', result); + return result; -## Webhook Notification Examples - - -### container.updated - -The container updated event lets you know about changes to container properties at the terminal, or which terminal the container is (or will be) located at. - -The `changeset` attribute on is a hash of all the properties which changed on the container. - -Each changed property is the hash key. The prior value is the first item in the array, and the current value is the second item in the array. - -For example: -``` -"changeset": { - "pickup_lfd": [null, "2020-05-20 00:00:00"] + } catch (error) { + console.error('Error registering webhook:', error); + throw error; + } } ``` -Shows that the pickup last free day has changed from not being set to May 20 2020. - -The properties we show changes for are: -- fees_at_pod_terminal -- holds_at_pod_terminal -- pickup_lfd -- pickup_appointment_at -- available_for_pickup -- pod_terminal - -In every case the attribute `container_updated.timestamp` tells you when we picked up the changes from the terminal. - - -As container availability becomes known or changes at the POD Terminal we will send `container_updated` events with the key `available_for_pickup` in the `changeset`. -```json -{ - "data": { - "id": "fa1a6731-4b34-4b0c-aabc-460892055ba1", - "type": "webhook_notification", - "attributes": { - "id": "fa1a6731-4b34-4b0c-aabc-460892055ba1", - "event": "container.updated", - "delivery_status": "pending", - "created_at": "2023-01-24T00:11:32Z" - }, - "relationships": { - "reference_object": { - "data": { - "id": "e8f1976c-0089-4b98-96ae-90aa87fbdfee", - "type": "container_updated_event" - } - }, - "webhook": { - "data": { - "id": "8a5ffa8f-3dc1-48de-a0ea-09fc4f2cd96f", - "type": "webhook" - } - }, - "webhook_notification_logs": { - "data": [ +```bash title="cURL Example" +curl -X POST \ + https://api.terminal49.com/v2/webhooks \ + -H 'Authorization: Token YOUR_API_KEY' \ + -H 'Content-Type: application/vnd.api+json' \ + -d '{ + "data": { + "type": "webhook", + "attributes": { + "url": "https://your-domain.com/terminal49-webhook", + "event_types": [ + "container.transport.vessel_arrived", + "container.transport.vessel_discharged", + "container.available_for_pickup", + "container.pickup_lfd.changed", + "container.holds.changed", + "tracking_request.succeeded", + "tracking_request.failed" ] } } - }, - "included": [ - { - "id": "adc08630-51d3-4bbc-a859-5157cbbe806c", - "type": "shipment", - "attributes": { - "created_at": "2023-01-24T00:11:32Z", - "ref_numbers": [ - "REF-50FFA3", - "REF-5AC291" - ], - "tags": [ - - ], - "bill_of_lading_number": "TE49DD306F13", - "normalized_number": "TE49DD306F13", - "shipping_line_scac": "MSCU", - "shipping_line_name": "Mediterranean Shipping Company", - "shipping_line_short_name": "MSC", - "port_of_lading_locode": "MXZLO", - "port_of_lading_name": "Manzanillo", - "port_of_discharge_locode": "USOAK", - "port_of_discharge_name": "Port of Oakland", - "pod_vessel_name": "MSC CHANNE", - "pod_vessel_imo": "9710438", - "pod_voyage_number": "098N", - "destination_locode": null, - "destination_name": null, - "destination_timezone": null, - "destination_ata_at": null, - "destination_eta_at": null, - "pol_etd_at": null, - "pol_atd_at": "2023-01-11T00:11:32Z", - "pol_timezone": "America/Mexico_City", - "pod_eta_at": "2023-01-23T20:11:32Z", - "pod_ata_at": "2023-01-23T23:11:32Z", - "pod_timezone": "America/Los_Angeles", - "line_tracking_last_attempted_at": null, - "line_tracking_last_succeeded_at": "2023-01-24T00:11:32Z", - "line_tracking_stopped_at": null, - "line_tracking_stopped_reason": null - }, - "relationships": { - "port_of_lading": { - "data": { - "id": "588711e2-3f78-4178-ae5e-ccb690e0671d", - "type": "port" - } - }, - "port_of_discharge": { - "data": { - "id": "9a25e0aa-52bd-4bb8-8876-cd7616f5fb0f", - "type": "port" - } - }, - "pod_terminal": { - "data": { - "id": "4960e227-93b1-4f85-bf7c-07c9b6f597e0", - "type": "terminal" - } - }, - "destination": { - "data": null - }, - "destination_terminal": { - "data": { - "id": "26d8be45-b428-45fa-819b-46c828bf6fac", - "type": "terminal" - } - }, - "line_tracking_stopped_by_user": { - "data": null - }, - "containers": { - "data": [ - { - "id": "3cd51f0e-eb18-4399-9f90-4c8a22250f63", - "type": "container" - } - ] - } - }, - "links": { - "self": "/v2/shipments/adc08630-51d3-4bbc-a859-5157cbbe806c" - } - }, - { - "id": "9a25e0aa-52bd-4bb8-8876-cd7616f5fb0f", - "type": "port", - "attributes": { - "id": "9a25e0aa-52bd-4bb8-8876-cd7616f5fb0f", - "name": "Port of Oakland", - "code": "USOAK", - "state_abbr": "CA", - "city": "Oakland", - "country_code": "US", - "time_zone": "America/Los_Angeles" - } - }, - { - "id": "4960e227-93b1-4f85-bf7c-07c9b6f597e0", - "type": "terminal", - "attributes": { - "id": "4960e227-93b1-4f85-bf7c-07c9b6f597e0", - "nickname": "SSA", - "name": "SSA Terminal", - "firms_code": "Z985" - }, - "relationships": { - "port": { - "data": { - "id": "9a25e0aa-52bd-4bb8-8876-cd7616f5fb0f", - "type": "port" - } - } - } - }, - { - "id": "3cd51f0e-eb18-4399-9f90-4c8a22250f63", - "type": "container", - "attributes": { - "number": "COSU1186800", - "seal_number": "43e29239e5dd5276", - "created_at": "2023-01-24T00:11:32Z", - "ref_numbers": [ - "REF-C86614", - "REF-456CEA" - ], - "pod_arrived_at": "2023-01-23T23:11:32Z", - "pod_discharged_at": "2023-01-24T00:11:32Z", - "final_destination_full_out_at": null, - "equipment_type": "dry", - "equipment_length": 40, - "equipment_height": "standard", - "weight_in_lbs": 43333, - "pod_full_out_at": null, - "empty_terminated_at": null, - "terminal_checked_at": null, - "fees_at_pod_terminal": [ - - ], - "holds_at_pod_terminal": [ - - ], - "pickup_lfd": null, - "pickup_appointment_at": null, - "pod_full_out_chassis_number": null, - "location_at_pod_terminal": null, - "availability_known": true, - "available_for_pickup": true, - "pod_timezone": "America/Los_Angeles", - "final_destination_timezone": null, - "empty_terminated_timezone": "America/Los_Angeles" - }, - "relationships": { - "shipment": { - "data": { - "id": "adc08630-51d3-4bbc-a859-5157cbbe806c", - "type": "shipment" - } - }, - "pod_terminal": { - "data": { - "id": "4960e227-93b1-4f85-bf7c-07c9b6f597e0", - "type": "terminal" - } - }, - "transport_events": { - "data": [ + }' +``` - ] - }, - "raw_events": { - "data": [ +### Step 3: Verify and Test Your Webhook - ] - } - } - }, - { - "id": "e8f1976c-0089-4b98-96ae-90aa87fbdfee", - "type": "container_updated_event", - "attributes": { - "changeset": { - "available_for_pickup": [ - false, - true - ] - }, - "timestamp": "2023-01-24T00:11:32Z", - "data_source": "terminal", - "timezone": "America/Los_Angeles" +After registering, you can test your webhook using Terminal49's test tracking numbers: + +```javascript title="Test Webhook" +async function testWebhook() { + try { + // Create a tracking request with a test number + const response = await fetch('https://api.terminal49.com/v2/tracking_requests', { + method: 'POST', + headers: { + 'Authorization': 'Token YOUR_API_KEY', + 'Content-Type': 'application/vnd.api+json' }, - "relationships": { - "container": { - "data": { - "id": "3cd51f0e-eb18-4399-9f90-4c8a22250f63", - "type": "container" - } - }, - "terminal": { - "data": { - "id": "4960e227-93b1-4f85-bf7c-07c9b6f597e0", - "type": "terminal" - } - }, - "shipment": { - "data": { - "id": "adc08630-51d3-4bbc-a859-5157cbbe806c", - "type": "shipment" + body: JSON.stringify({ + data: { + type: "tracking_request", + attributes: { + tracking_number: "TEST-ARRIVAL-TODAY", // Test number + reference_number: "TEST-ORDER-123" } } - } - } - ] + }) + }); + + const result = await response.json(); + console.log('Test tracking request created:', result); + + } catch (error) { + console.error('Error creating test tracking request:', error); + } } ``` -The `pod_terminal` is a relationship of the container. When the pod_terminal changes the id is included. The terminal will be serialized in the included models. +## Managing Webhooks -N.B. the `container_updated_event` also has a relationship to a `terminal` which refers to where the information came from. Currently this is always the POD terminal. In the future this may be the final destination terminal or an off-dock location. -```json -{ - "data": { - "id": "f6c5e340-94bf-4681-a47d-f2e8d6c90e59", - "type": "webhook_notification", - "attributes": { - "id": "f6c5e340-94bf-4681-a47d-f2e8d6c90e59", - "event": "container.updated", - "delivery_status": "pending", - "created_at": "2023-01-24T00:13:06Z" - }, - "relationships": { - "reference_object": { - "data": { - "id": "567eccef-53bf-43d5-b3d8-00278d7710df", - "type": "container_updated_event" - } - }, - "webhook": { - "data": { - "id": "2e5f41d1-8a3b-4940-a9bb-ff0481e09c71", - "type": "webhook" - } - }, - "webhook_notification_logs": { - "data": [ +### Listing Your Webhooks - ] - } - } - }, - "included": [ - { - "id": "c74ff2a5-5ede-4fc2-886b-3eeef886ff32", - "type": "shipment", - "attributes": { - "created_at": "2023-01-24T00:13:05Z", - "ref_numbers": [ - "REF-29557A" - ], - "tags": [ - - ], - "bill_of_lading_number": "TE497F86D5B7", - "normalized_number": "TE497F86D5B7", - "shipping_line_scac": "MSCU", - "shipping_line_name": "Mediterranean Shipping Company", - "shipping_line_short_name": "MSC", - "port_of_lading_locode": "MXZLO", - "port_of_lading_name": "Manzanillo", - "port_of_discharge_locode": "USOAK", - "port_of_discharge_name": "Port of Oakland", - "pod_vessel_name": "MSC CHANNE", - "pod_vessel_imo": "9710438", - "pod_voyage_number": "098N", - "destination_locode": null, - "destination_name": null, - "destination_timezone": null, - "destination_ata_at": null, - "destination_eta_at": null, - "pol_etd_at": null, - "pol_atd_at": "2023-01-11T00:13:05Z", - "pol_timezone": "America/Mexico_City", - "pod_eta_at": "2023-01-23T21:13:05Z", - "pod_ata_at": "2023-01-24T00:13:05Z", - "pod_timezone": "America/Los_Angeles", - "line_tracking_last_attempted_at": null, - "line_tracking_last_succeeded_at": "2023-01-24T00:13:05Z", - "line_tracking_stopped_at": null, - "line_tracking_stopped_reason": null - }, - "relationships": { - "port_of_lading": { - "data": { - "id": "8d0f0cba-9961-4fa5-9bf0-0fb5fb67bdbe", - "type": "port" - } - }, - "port_of_discharge": { - "data": { - "id": "9722a830-634e-4f7a-b1b3-793ccaf8cbb2", - "type": "port" - } - }, - "pod_terminal": { - "data": { - "id": "08831e36-766b-4ac8-8235-d8594b55ff6d", - "type": "terminal" - } - }, - "destination": { - "data": null - }, - "destination_terminal": { - "data": { - "id": "f2a6a6e2-4bd1-4c66-aa8b-be4cb2ddc9a8", - "type": "terminal" - } - }, - "line_tracking_stopped_by_user": { - "data": null - }, - "containers": { - "data": [ - { - "id": "adf4673d-f4ba-41a9-82da-55c0ae3b3722", - "type": "container" - } - ] - } - }, - "links": { - "self": "/v2/shipments/c74ff2a5-5ede-4fc2-886b-3eeef886ff32" - } - }, - { - "id": "9722a830-634e-4f7a-b1b3-793ccaf8cbb2", - "type": "port", - "attributes": { - "id": "9722a830-634e-4f7a-b1b3-793ccaf8cbb2", - "name": "Port of Oakland", - "code": "USOAK", - "state_abbr": "CA", - "city": "Oakland", - "country_code": "US", - "time_zone": "America/Los_Angeles" - } - }, - { - "id": "08831e36-766b-4ac8-8235-d8594b55ff6d", - "type": "terminal", - "attributes": { - "id": "08831e36-766b-4ac8-8235-d8594b55ff6d", - "nickname": "STO", - "name": "Shippers Transport Express", - "firms_code": "STO" - }, - "relationships": { - "port": { - "data": { - "id": "9722a830-634e-4f7a-b1b3-793ccaf8cbb2", - "type": "port" - } - } +You can retrieve a list of all registered webhooks: + +```javascript title="List Webhooks" +async function listWebhooks() { + try { + const response = await fetch('https://api.terminal49.com/v2/webhooks', { + headers: { + 'Authorization': 'Token YOUR_API_KEY' } - }, - { - "id": "adf4673d-f4ba-41a9-82da-55c0ae3b3722", - "type": "container", - "attributes": { - "number": "CGMU1560506", - "seal_number": "a9948b719482648c", - "created_at": "2023-01-24T00:13:06Z", - "ref_numbers": [ - "REF-D2AC6F", - "REF-34E84B" - ], - "pod_arrived_at": "2023-01-24T00:13:05Z", - "pod_discharged_at": "2023-01-24T00:13:05Z", - "final_destination_full_out_at": null, - "equipment_type": "dry", - "equipment_length": 40, - "equipment_height": "standard", - "weight_in_lbs": 43481, - "pod_full_out_at": null, - "empty_terminated_at": null, - "terminal_checked_at": null, - "fees_at_pod_terminal": [ - - ], - "holds_at_pod_terminal": [ - - ], - "pickup_lfd": null, - "pickup_appointment_at": null, - "pod_full_out_chassis_number": null, - "location_at_pod_terminal": null, - "availability_known": true, - "available_for_pickup": true, - "pod_timezone": "America/Los_Angeles", - "final_destination_timezone": null, - "empty_terminated_timezone": "America/Los_Angeles" - }, - "relationships": { - "shipment": { - "data": { - "id": "c74ff2a5-5ede-4fc2-886b-3eeef886ff32", - "type": "shipment" - } - }, - "pod_terminal": { - "data": { - "id": "08831e36-766b-4ac8-8235-d8594b55ff6d", - "type": "terminal" - } - }, - "transport_events": { - "data": [ + }); - ] - }, - "raw_events": { - "data": [ + const result = await response.json(); + console.log('Your webhooks:', result.data); + return result.data; - ] - } - } - }, - { - "id": "0ef5519f-1b39-4f6c-9961-1bbba0ac1307", - "type": "terminal", - "attributes": { - "id": "0ef5519f-1b39-4f6c-9961-1bbba0ac1307", - "nickname": "SSA", - "name": "SSA Terminal", - "firms_code": "Z985" - }, - "relationships": { - "port": { - "data": { - "id": "9722a830-634e-4f7a-b1b3-793ccaf8cbb2", - "type": "port" - } - } - } - }, - { - "id": "567eccef-53bf-43d5-b3d8-00278d7710df", - "type": "container_updated_event", - "attributes": { - "changeset": { - "pod_terminal": [ - "0ef5519f-1b39-4f6c-9961-1bbba0ac1307", - "08831e36-766b-4ac8-8235-d8594b55ff6d" - ] - }, - "timestamp": "2023-01-24T00:13:06Z", - "data_source": "terminal", - "timezone": "America/Los_Angeles" - }, - "relationships": { - "container": { - "data": { - "id": "adf4673d-f4ba-41a9-82da-55c0ae3b3722", - "type": "container" - } - }, - "terminal": { - "data": { - "id": "0ef5519f-1b39-4f6c-9961-1bbba0ac1307", - "type": "terminal" - } - }, - "shipment": { - "data": { - "id": "c74ff2a5-5ede-4fc2-886b-3eeef886ff32", - "type": "shipment" - } - } - } - } - ] + } catch (error) { + console.error('Error listing webhooks:', error); + throw error; + } } ``` - -### tracking_request.succeeded - -```json -{ - "data": { - "id": "a76187fc-5749-43f9-9053-cfaad9790a31", - "type": "webhook_notification", - "attributes": { - "id": "a76187fc-5749-43f9-9053-cfaad9790a31", - "event": "tracking_request.succeeded", - "delivery_status": "pending", - "created_at": "2020-09-11T21:25:34Z" - }, - "relationships": { - "reference_object": { - "data": { - "id": "bdeca506-9741-4ab1-a0a7-cfd1d908e923", - "type": "tracking_request" - } - }, - "webhook": { - "data": { - "id": "914b21ce-dd7d-4c49-8503-65aba488e9a9", - "type": "webhook" - } - }, - "webhook_notification_logs": { - "data": [] - } - } - }, - "included": [ - { - "id": "bdeca506-9741-4ab1-a0a7-cfd1d908e923", - "type": "tracking_request", - "attributes": { - "request_number": "TE497ED1063E", - "request_type": "bill_of_lading", - "scac": "MSCU", - "ref_numbers": [], - "created_at": "2020-09-11T21:25:34Z", - "updated_at": "2020-09-11T22:25:34Z", - "status": "created", - "failed_reason": null, - "is_retrying": false, - "retry_count": null - }, - "relationships": { - "tracked_object": { - "data": { - "id": "b5b10c0a-8d18-46da-b4c2-4e5fa790e7da", - "type": "shipment" +### Updating a Webhook + +To update an existing webhook (e.g., to change the URL or event types): + +```javascript title="Update Webhook" +async function updateWebhook(webhookId) { + try { + const response = await fetch(`https://api.terminal49.com/v2/webhooks/${webhookId}`, { + method: 'PATCH', + headers: { + 'Authorization': 'Token YOUR_API_KEY', + 'Content-Type': 'application/vnd.api+json' + }, + body: JSON.stringify({ + data: { + id: webhookId, + type: "webhook", + attributes: { + url: "https://your-updated-domain.com/webhook", + event_types: [ + "container.transport.vessel_arrived", + "container.available_for_pickup" + // Updated list of events + ] } } - }, - "links": { - "self": "/v2/tracking_requests/bdeca506-9741-4ab1-a0a7-cfd1d908e923" - } - }, - { - "id": "b5b10c0a-8d18-46da-b4c2-4e5fa790e7da", - "type": "shipment", - "attributes": { - "created_at": "2020-09-11T21:25:33Z", - "bill_of_lading_number": "TE497ED1063E", - "ref_numbers": [], - "shipping_line_scac": "MSCU", - "shipping_line_name": "Mediterranean Shipping Company", - "port_of_lading_locode": "MXZLO", - "port_of_lading_name": "Manzanillo", - "port_of_discharge_locode": "USOAK", - "port_of_discharge_name": "Port of Oakland", - "pod_vessel_name": "MSC CHANNE", - "pod_vessel_imo": "9710438", - "pod_voyage_number": "098N", - "destination_locode": null, - "destination_name": null, - "destination_timezone": null, - "destination_ata_at": null, - "destination_eta_at": null, - "pol_etd_at": null, - "pol_atd_at": "2020-08-29T21:25:33Z", - "pol_timezone": "America/Mexico_City", - "pod_eta_at": "2020-09-18T21:25:33Z", - "pod_ata_at": null, - "pod_timezone": "America/Los_Angeles" - }, - "relationships": { - "port_of_lading": { - "data": { - "id": "4384d6a5-5ccc-43b7-8d19-4a9525e74c08", - "type": "port" - } - }, - "port_of_discharge": { - "data": { - "id": "2a765fdd-c479-4345-b71d-c4ef839952e2", - "type": "port" - } - }, - "pod_terminal": { - "data": { - "id": "17891bc8-52da-40bf-8ff0-0247ec05faf1", - "type": "terminal" - } - }, - "destination": { - "data": null - }, - "containers": { - "data": [ - { - "id": "b2fc728c-e2f5-4a99-8899-eb7b34ef22d7", - "type": "container" - } - ] - } - }, - "links": { - "self": "/v2/shipments/b5b10c0a-8d18-46da-b4c2-4e5fa790e7da" - } - }, - { - "id": "b2fc728c-e2f5-4a99-8899-eb7b34ef22d7", - "type": "container", - "attributes": { - "number": "ARDU1824900", - "seal_number": "139F1451", - "created_at": "2020-09-11T21:25:34Z", - "equipment_type": "dry", - "equipment_length": 40, - "equipment_height": "standard", - "weight_in_lbs": 53507, - "fees_at_pod_terminal": [], - "holds_at_pod_terminal": [], - "pickup_lfd": null, - "pickup_appointment_at": null, - "availability_known": true, - "available_for_pickup": false, - "pod_arrived_at": null, - "pod_discharged_at": null, - "location_at_pod_terminal": null, - "final_destination_full_out_at": null, - "pod_full_out_at": null, - "empty_terminated_at": null - }, - "relationships": { - "shipment": { - "data": { - "id": "b5b10c0a-8d18-46da-b4c2-4e5fa790e7da", - "type": "shipment" - } - }, - "pod_terminal": { - "data": { - "id": "17891bc8-52da-40bf-8ff0-0247ec05faf1", - "type": "terminal" - } - }, - "transport_events": { - "data": [ - { - "id": "56078596-5293-4c84-9245-cca00a787265", - "type": "transport_event" - } - ] - } - } - }, - { - "id": "56078596-5293-4c84-9245-cca00a787265", - "type": "transport_event", - "attributes": { - "event": "container.transport.vessel_departed", - "created_at": "2020-09-11T21:25:34Z", - "voyage_number": null, - "timestamp": "2020-08-29T21:25:33Z", - "location_locode": "MXZLO", - "timezone": "America/Los_Angeles" - }, - "relationships": { - "shipment": { - "data": { - "id": "b5b10c0a-8d18-46da-b4c2-4e5fa790e7da", - "type": "shipment" - } - }, - "container": { - "data": { - "id": "b2fc728c-e2f5-4a99-8899-eb7b34ef22d7", - "type": "container" - } - }, - "vessel": { - "data": null - }, - "location": { - "data": { - "id": "2a765fdd-c479-4345-b71d-c4ef839952e2", - "type": "port" - } - }, - "terminal": { - "data": null - } - } - } - ] + }) + }); + + const result = await response.json(); + console.log('Webhook updated:', result); + return result; + + } catch (error) { + console.error('Error updating webhook:', error); + throw error; + } } ``` -### shipment.estimated.arrival +### Deleting a Webhook -```json -{ - "data": { - "id": "b03bcf3c-252d-41f8-b86f-939b404e304b", - "type": "webhook_notification", - "attributes": { - "id": "b03bcf3c-252d-41f8-b86f-939b404e304b", - "event": "shipment.estimated.arrival", - "delivery_status": "pending", - "created_at": "2022-01-13T19:56:58Z" - }, - "relationships": { - "reference_object": { - "data": { - "id": "14b5047f-e3e7-4df7-a570-2d3878e6d863", - "type": "estimated_event" - } - }, - "webhook": { - "data": { - "id": "d60a23a4-f40d-44d2-8b6a-2e55a527e6a2", - "type": "webhook" - } - }, - "webhook_notification_logs": { - "data": [ +If you no longer need a webhook, you can delete it: - ] +```javascript title="Delete Webhook" +async function deleteWebhook(webhookId) { + try { + const response = await fetch(`https://api.terminal49.com/v2/webhooks/${webhookId}`, { + method: 'DELETE', + headers: { + 'Authorization': 'Token YOUR_API_KEY' } + }); + + if (response.status === 204) { + console.log('Webhook deleted successfully'); + return true; + } else { + console.error('Failed to delete webhook:', response.status); + return false; } - }, - "included": [ - { - "id": "14b5047f-e3e7-4df7-a570-2d3878e6d863", - "type": "estimated_event", - "attributes": { - "created_at": "2022-01-13T19:56:58Z", - "estimated_timestamp": "2022-01-16T19:56:58Z", - "voyage_number": "098N", - "event": "shipment.estimated.arrival", - "location_locode": "USOAK", - "timezone": "America/Los_Angeles" - }, - "relationships": { - "shipment": { - "data": { - "id": "8e4a1f1e-aa13-4cad-9df0-aec6c791a5f8", - "type": "shipment" - } - }, - "port": { - "data": { - "id": "3ee88ea1-3b8b-4b96-80fb-6aa23ba7065e", - "type": "port" - } - }, - "vessel": { - "data": { - "id": "b1550abc-4e73-4271-a0f4-8ac031f242cd", - "type": "vessel" - } - } - } - }, - { - "id": "3ee88ea1-3b8b-4b96-80fb-6aa23ba7065e", - "type": "port", - "attributes": { - "id": "3ee88ea1-3b8b-4b96-80fb-6aa23ba7065e", - "name": "Port of Oakland", - "code": "USOAK", - "state_abbr": "CA", - "city": "Oakland", - "country_code": "US", - "time_zone": "America/Los_Angeles" - } - }, - { - "id": "8e4a1f1e-aa13-4cad-9df0-aec6c791a5f8", - "type": "shipment", - "attributes": { - "created_at": "2022-01-13T19:56:58Z", - "ref_numbers": [ - "REF-3AA505", - "REF-910757", - "REF-2A8357" - ], - "tags": [ - - ], - "bill_of_lading_number": "TE49C31E16E2", - "shipping_line_scac": "MSCU", - "shipping_line_name": "Mediterranean Shipping Company", - "shipping_line_short_name": "MSC", - "port_of_lading_locode": "MXZLO", - "port_of_lading_name": "Manzanillo", - "port_of_discharge_locode": "USOAK", - "port_of_discharge_name": "Port of Oakland", - "pod_vessel_name": "MSC CHANNE", - "pod_vessel_imo": "9710438", - "pod_voyage_number": "098N", - "destination_locode": null, - "destination_name": null, - "destination_timezone": null, - "destination_ata_at": null, - "destination_eta_at": null, - "pol_etd_at": null, - "pol_atd_at": "2021-12-31T19:56:58Z", - "pol_timezone": "America/Mexico_City", - "pod_eta_at": "2022-01-16T19:56:58Z", - "pod_ata_at": null, - "pod_timezone": "America/Los_Angeles", - "line_tracking_last_attempted_at": null, - "line_tracking_last_succeeded_at": "2022-01-13T19:56:58Z", - "line_tracking_stopped_at": null, - "line_tracking_stopped_reason": null - }, - "relationships": { - "port_of_lading": { - "data": { - "id": "78ad2915-700b-4919-8ede-a3b6c2137436", - "type": "port" - } - }, - "port_of_discharge": { - "data": { - "id": "3ee88ea1-3b8b-4b96-80fb-6aa23ba7065e", - "type": "port" - } - }, - "pod_terminal": { - "data": { - "id": "3bd88777-48ea-4880-9cb9-961dd4d26a00", - "type": "terminal" - } - }, - "destination": { - "data": null - }, - "destination_terminal": { - "data": { - "id": "1d016b3d-96d5-4867-8f99-77233d1cc57d", - "type": "terminal" - } - }, - "containers": { - "data": [ - ] - } - }, - "links": { - "self": "/v2/shipments/8e4a1f1e-aa13-4cad-9df0-aec6c791a5f8" - } - } - ] + } catch (error) { + console.error('Error deleting webhook:', error); + throw error; + } } ``` -### container.transport.vessel_arrived +## Webhook Payload Structure -```json +When an event occurs, Terminal49 sends a webhook notification with a JSON:API structured payload: + +```json title="Example Payload" { "data": { - "id": "72f8b0b5-28f5-4a12-8274-71d4d23c9ab7", + "id": "123e4567-e89b-12d3-a456-426614174000", "type": "webhook_notification", "attributes": { - "id": "72f8b0b5-28f5-4a12-8274-71d4d23c9ab7", "event": "container.transport.vessel_arrived", - "delivery_status": "pending", - "created_at": "2023-01-24T00:14:28Z" + "created_at": "2023-05-15T14:23:12Z" }, "relationships": { "reference_object": { "data": { - "id": "c1443820-304a-444b-bf42-c3d885dc8daa", - "type": "transport_event" - } - }, - "webhook": { - "data": { - "id": "655236f8-7936-4611-b580-341d3e1103f5", - "type": "webhook" + "id": "789e4567-e89b-12d3-a456-426614174000", + "type": "container" } - }, - "webhook_notification_logs": { - "data": [ - - ] } } }, "included": [ { - "id": "290a696b-5fba-45aa-a08c-0e15ae89e9c0", - "type": "shipment", - "attributes": { - "created_at": "2023-01-24T00:14:28Z", - "ref_numbers": [ - "REF-134938", - "REF-BE2704", - "REF-712D47" - ], - "tags": [ - - ], - "bill_of_lading_number": "TE49735F4B1D", - "normalized_number": "TE49735F4B1D", - "shipping_line_scac": "MSCU", - "shipping_line_name": "Mediterranean Shipping Company", - "shipping_line_short_name": "MSC", - "port_of_lading_locode": "MXZLO", - "port_of_lading_name": "Manzanillo", - "port_of_discharge_locode": "USOAK", - "port_of_discharge_name": "Port of Oakland", - "pod_vessel_name": "MSC CHANNE", - "pod_vessel_imo": "9710438", - "pod_voyage_number": "098N", - "destination_locode": null, - "destination_name": null, - "destination_timezone": null, - "destination_ata_at": null, - "destination_eta_at": null, - "pol_etd_at": null, - "pol_atd_at": "2023-01-11T00:14:28Z", - "pol_timezone": "America/Mexico_City", - "pod_eta_at": "2023-01-31T00:14:28Z", - "pod_ata_at": "2023-01-31T01:14:28Z", - "pod_timezone": "America/Los_Angeles", - "line_tracking_last_attempted_at": null, - "line_tracking_last_succeeded_at": "2023-01-24T00:14:28Z", - "line_tracking_stopped_at": null, - "line_tracking_stopped_reason": null - }, - "relationships": { - "port_of_lading": { - "data": { - "id": "036084b7-f2cc-49b5-9d81-7de2cdabfc69", - "type": "port" - } - }, - "port_of_discharge": { - "data": { - "id": "0e0c9ad6-ec83-48b3-87f9-c2710659821b", - "type": "port" - } - }, - "pod_terminal": { - "data": { - "id": "1ee2022a-e054-4f76-8c1a-60967e76b407", - "type": "terminal" - } - }, - "destination": { - "data": null - }, - "destination_terminal": { - "data": { - "id": "b07e8193-47cf-4395-a1f6-a5d4d7fa9b17", - "type": "terminal" - } - }, - "line_tracking_stopped_by_user": { - "data": null - }, - "containers": { - "data": [ - { - "id": "c8fa5c2a-1bd0-48d8-8c94-2ef8a06c4ce9", - "type": "container" - } - ] - } - }, - "links": { - "self": "/v2/shipments/290a696b-5fba-45aa-a08c-0e15ae89e9c0" - } - }, - { - "id": "c8fa5c2a-1bd0-48d8-8c94-2ef8a06c4ce9", + "id": "789e4567-e89b-12d3-a456-426614174000", "type": "container", "attributes": { - "number": "GLDU1222600", - "seal_number": "d5103634ed1adbd4", - "created_at": "2023-01-24T00:14:28Z", - "ref_numbers": [ - "REF-889564" - ], - "pod_arrived_at": "2023-01-24T00:14:28Z", - "pod_discharged_at": "2023-01-24T00:14:28Z", - "final_destination_full_out_at": "2023-01-24T00:14:28Z", - "equipment_type": "dry", - "equipment_length": 40, - "equipment_height": "standard", - "weight_in_lbs": 46679, - "pod_full_out_at": null, - "empty_terminated_at": null, - "terminal_checked_at": null, - "fees_at_pod_terminal": [ - - ], - "holds_at_pod_terminal": [ - - ], - "pickup_lfd": null, - "pickup_appointment_at": null, - "pod_full_out_chassis_number": null, - "location_at_pod_terminal": null, - "availability_known": true, - "available_for_pickup": false, - "pod_timezone": "America/Los_Angeles", - "final_destination_timezone": null, - "empty_terminated_timezone": "America/Los_Angeles" + "number": "MSCU1234567", + "seal_number": "SEAL123456", + "created_at": "2023-05-10T10:23:12Z", + "updated_at": "2023-05-15T14:23:12Z" }, "relationships": { "shipment": { "data": { - "id": "290a696b-5fba-45aa-a08c-0e15ae89e9c0", + "id": "456e4567-e89b-12d3-a456-426614174000", "type": "shipment" } - }, - "pod_terminal": { - "data": null - }, - "transport_events": { - "data": [ - { - "id": "c1443820-304a-444b-bf42-c3d885dc8daa", - "type": "transport_event" - } - ] - }, - "raw_events": { - "data": [ - - ] - } - } - }, - { - "id": "0e0c9ad6-ec83-48b3-87f9-c2710659821b", - "type": "port", - "attributes": { - "id": "0e0c9ad6-ec83-48b3-87f9-c2710659821b", - "name": "Port of Oakland", - "code": "USOAK", - "state_abbr": "CA", - "city": "Oakland", - "country_code": "US", - "time_zone": "America/Los_Angeles" - } - }, - { - "id": "1ee2022a-e054-4f76-8c1a-60967e76b407", - "type": "terminal", - "attributes": { - "id": "1ee2022a-e054-4f76-8c1a-60967e76b407", - "nickname": "SSA", - "name": "SSA Terminal", - "firms_code": "Z985" - }, - "relationships": { - "port": { - "data": { - "id": "0e0c9ad6-ec83-48b3-87f9-c2710659821b", - "type": "port" - } - } - } - }, - { - "id": "100c303e-79df-4301-9bf7-13f9e0c85851", - "type": "vessel", - "attributes": { - "name": "MSC CHANNE", - "imo": "9710438", - "mmsi": "255805864", - "latitude": -78.30435842851921, - "longitude": 25.471353799804547, - "nautical_speed_knots": 100, - "navigational_heading_degrees": 1, - "position_timestamp": "2023-06-05T19:46:18Z" - } - }, - { - "id": "c1443820-304a-444b-bf42-c3d885dc8daa", - "type": "transport_event", - "attributes": { - "event": "container.transport.vessel_arrived", - "created_at": "2023-01-24T00:14:27Z", - "voyage_number": null, - "timestamp": "2023-01-24T00:14:27Z", - "data_source": "shipping_line", - "location_locode": "USOAK", - "timezone": "America/Los_Angeles" - }, - "relationships": { - "shipment": { - "data": { - "id": "290a696b-5fba-45aa-a08c-0e15ae89e9c0", - "type": "shipment" - } - }, - "container": { - "data": { - "id": "c8fa5c2a-1bd0-48d8-8c94-2ef8a06c4ce9", - "type": "container" - } - }, - "vessel": { - "data": { - "id": "100c303e-79df-4301-9bf7-13f9e0c85851", - "type": "vessel" - } - }, - "location": { - "data": { - "id": "0e0c9ad6-ec83-48b3-87f9-c2710659821b", - "type": "port" - } - }, - "terminal": { - "data": { - "id": "1ee2022a-e054-4f76-8c1a-60967e76b407", - "type": "terminal" - } } } } ] } -``` \ No newline at end of file +``` + +## Webhook Event Types + +Terminal49 supports multiple event types that you can subscribe to: + + + - `container.created`: A new container is tracked in the system + - `container.estimated_arrival_changed`: The estimated arrival date has changed + - `container.transport.vessel_arrived`: The vessel carrying the container has arrived at port + - `container.transport.vessel_discharged`: The container has been unloaded from vessel + - `container.available_for_pickup`: The container is available for pickup from terminal + - `container.pickup_lfd.changed`: Last Free Day for pickup has changed + - `container.holds.changed`: Customs or terminal holds have changed + - `container.picked_up`: The container has been picked up from terminal + - `container.empty_returned`: The empty container has been returned to terminal + + + + - `tracking_request.succeeded`: A tracking request was successful + - `tracking_request.failed`: A tracking request failed to find container information + + + + - `shipment.created`: A new shipment is created in the system + - `shipment.updated`: A shipment's details have been updated + + +## Best Practices + + + Your webhook endpoint should acknowledge receipt of a webhook by returning a 2xx HTTP status code (200, 201, 202, or 204) as quickly as possible. Process the webhook data asynchronously after acknowledging receipt. + + + + Design your webhook handler to be idempotent, meaning it handles duplicate webhook deliveries gracefully. Terminal49 may retry webhook delivery if your server doesn't respond with a success code. + + + + - Use HTTPS for your webhook endpoint + - Consider implementing webhook signature validation if available + - Only accept webhooks from Terminal49's IP ranges if possible + + + + Handle errors gracefully and set up monitoring for your webhook endpoint. If webhooks aren't being received as expected, check your server logs and Terminal49 dashboard. + + + + Implement a polling fallback that periodically checks the API directly in case webhooks are missed or delayed. This ensures your data stays up-to-date even if webhook delivery fails. + + +## Troubleshooting + + + - Check if your webhook URL is accessible from the internet + - Verify your server is returning 2xx status codes + - Check if the webhook is registered correctly + - Look for any firewalls or security measures blocking incoming requests + + + + Terminal49 may retry webhook delivery if your server doesn't respond or returns an error. Implement idempotency in your handler to prevent processing the same event multiple times. + + + + Use Terminal49's [test tracking numbers](/api-docs/useful-info/test-numbers) to simulate events and test your webhook handling without waiting for real events to occur. + + +## Next Steps + + + + Compare different approaches for receiving updates from Terminal49. + + + View the complete webhook API reference. + + + Learn about test tracking numbers to simulate events. + + + Understand how webhooks relate to Terminal49's data model. + + diff --git a/docs/api-docs/overview.mdx b/docs/api-docs/overview.mdx new file mode 100644 index 00000000..bcb8f111 --- /dev/null +++ b/docs/api-docs/overview.mdx @@ -0,0 +1,251 @@ +--- +title: Overview +description: A comprehensive overview of Terminal49's integration options, capabilities, and use cases for container tracking and visibility. +icon: "ship" +--- + + +Get real-time container tracking visibility across ocean carriers, terminals, and rail carriers in one unified API. Start tracking your first container in minutes. + + +# Terminal49 Integration Overview + +Terminal49 provides comprehensive container tracking and visibility solutions through multiple integration options. This overview will help you understand what's possible and how to choose the right approach for your business needs. + + + + + Terminal49 API Integration Flow + + + Connect directly with our RESTful API to track specific shipments and containers, and receive real-time updates via webhooks. + + **Best for:** + - Custom workflows and applications + - Fine-grained control over tracking + - Real-time event notifications + - Integration with your own systems + + [Explore API Documentation](/api-docs/in-depth-guides/quickstart) + + + + Terminal49 DataSync Flow + + + Get tables of fresh information delivered directly into your existing data systems. Easy to set up and perfect for complementing your current data infrastructure. + + **Best for:** + - Data warehouse integration + - Business intelligence tools + - Custom reporting + - Bulk historical analysis + + [Learn About DataSync](/datasync/overview) + + + + +If you already have a data store that feeds the rest of your system, DataSync is likely your best option. If you need more granular control or custom workflows, direct API integration is recommended. + + +## Core Capabilities + + + + Track containers and shipments across their entire journey using bill of lading, booking, or container numbers. + + + Receive real-time updates when container milestone status changes, from origin to destination. + + + Access details on vessels, voyages, transshipments, and inland journey including timestamps. + + + Direct integrations across the entire rail network including rail milestones and rail terminal data. + + + Firms code, discharge, available for pickup, LFD, holds, fees, chassis, appointment and other information from ocean and rail terminals. + + + Get comprehensive shipment and container information including status, location, free time, and holds. + + + +## What Can I Use Terminal49 Data For? + + + + Track by MBOL, booking, or container number and get all shipment routing details including ports and inland locations. + + [Learn More](/api-docs/in-depth-guides/use-cases#getting-container-etas) + + + Get continuously updated vessel departure (ETD) and arrival (ETA) events to maintain accurate delivery schedules. + + [Learn More](/api-docs/in-depth-guides/use-cases#vessel-tracking) + + + Receive immediate notification when containers are available for pickup at the terminal. + + [Learn More](/api-docs/in-depth-guides/use-cases#container-availability) + + + Identify carrier and customs holds even before containers arrive at port to expedite release and avoid delays. + + [Learn More](/api-docs/in-depth-guides/use-cases#terminal-holds) + + + Monitor container return deadlines and locations to avoid per-diem charges and optimize asset utilization. + + [Learn More](/api-docs/in-depth-guides/use-cases) + + + Stay ahead of demurrage charges with notifications about containers approaching their last free day. + + [Learn More](/api-docs/in-depth-guides/use-cases#container-availability) + + + + + Our customers integrate Terminal49 data into their TMS, ERP, and customer portals to provide better visibility across the entire container journey. From proactively managing exceptions to optimizing pickup schedules, Terminal49 helps you maintain control over your supply chain. + + **Common integration patterns:** + - **Direct API integration** with your TMS or logistics platform + - **Custom portals** powered by our API for your customers + - **Automated notifications** for key stakeholders about shipment status + - **Exception dashboards** to highlight containers needing attention + - **Data feeds** into your data warehouse for analytics and reporting + + +## End-to-End Visibility + + + Terminal49 provides continuous visibility across the entire container journey, from origin to destination. + + + + +
+ Origin + Track empty container pickup, loading, and departure from origin port. +
+
+ +
+ Ocean + Follow vessel movements and receive ETA updates throughout the journey. +
+
+ +
+ Port + Get notified when the vessel arrives, berths, and when containers are discharged. +
+
+ +
+ Terminal + Monitor container availability, customs status, holds, and last free day information. +
+
+ +
+ Inland + Track rail departures, arrivals, and final delivery events to inland destinations. +
+
+
+ +## How It Works + + + ![Terminal49 Integration Flow Diagram](/images/api-flow-diagram.png) + + + + + Provide bill of lading, booking, or container numbers to track your shipments + + + Our system aggregates data from multiple sources including carriers, terminals, and rail operators + + + Your systems receive updates through webhooks or DataSync when any change occurs + + + Query the API or your data warehouse for detailed information as needed + + + +## Quick Start + +```bash +# Track a container using cURL +curl -X POST "https://api.terminal49.com/v2/tracking_requests" \ + -H "Authorization: Token YOUR_API_KEY" \ + -H "Content-Type: application/vnd.api+json" \ + -d '{ + "data": { + "type": "tracking_request", + "attributes": { + "reference_number": "MSCU1234567", + "reference_type": "container" + } + } + }' +``` + + + Get your API key by [signing up for an account](https://app.terminal49.com/register) and navigating to the API section. + + +## Common Use Cases + + + + Connect Terminal49 to your Transportation Management System for real-time visibility without manual tracking. + + + + Build automated alerts to notify customers about shipment status changes, delays, or container availability. + + + + Prioritize container pickups based on last free day information to avoid demurrage charges. + + + + Identify and resolve customs holds, freight issues, or delays that require intervention. + + + + Monitor containers on rail to coordinate inland pickups efficiently. + + + + Analyze carrier performance, transit times, and dwell times to optimize your supply chain. + + + +## Get Started + +Ready to start integrating with Terminal49? Choose your path: + + + + Follow our step-by-step guide to start tracking your first container in minutes. + + + + See how businesses are using Terminal49 to solve real logistics challenges. + + + + Dive into the technical documentation for Terminal49's API endpoints. + + + + Our team is here to help you get the most out of Terminal49. + + diff --git a/docs/docs.json b/docs/docs.json new file mode 100644 index 00000000..a0da3d93 --- /dev/null +++ b/docs/docs.json @@ -0,0 +1,304 @@ +{ + "$schema": "https://mintlify.com/docs.json", + "theme": "palm", + "name": "Terminal 49", + "colors": { + "primary": "#2bc983", + "light": "#52bfff", + "dark": "#00A2FF" + }, + "favicon": "/logos/favicon.svg", + "navigation": { + "tabs": [ + { + "tab": "Integration Guide", + "groups": [ + { + "group": "Overview", + "pages": [ + "api-docs/overview", + "api-docs/in-depth-guides/quickstart", + "api-docs/getting-started/tracking" + ] + }, + { + "group": "Guides", + "pages": [ + "api-docs/in-depth-guides/use-cases", + "api-docs/in-depth-guides/polling-vs-webhooks", + "api-docs/in-depth-guides/webhooks", + "api-docs/getting-started/list-shipments-and-containers", + "api-docs/getting-started/receive-status-updates", + "api-docs/in-depth-guides/adding-customer", + "api-docs/in-depth-guides/tracking-request-lifecycle", + "api-docs/in-depth-guides/event-timestamps", + "api-docs/in-depth-guides/terminal49-map", + "api-docs/in-depth-guides/terminal49-widget", + "api-docs/in-depth-guides/rail-integration-guide" + ] + }, + { + "group": "Useful Info", + "pages": [ + "api-docs/useful-info/api-data-sources-availability", + "api-docs/useful-info/pricing", + "api-docs/useful-info/test-numbers", + "api-docs/useful-info/tracking-request-retrying", + "api-docs/useful-info/webhook-events-examples" + ] + } + ] + }, + { + "tab": "API Reference", + "groups": [ + { + "group": "API Concepts", + "pages": [ + "api-docs/in-depth-guides/authentication", + "api-docs/in-depth-guides/error-handling", + "api-docs/in-depth-guides/rate-limiting", + "api-docs/in-depth-guides/json-api-guide", + "api-docs/in-depth-guides/including-resources" + ] + }, + { + "group": "Shipments", + "pages": [ + "api-docs/api-reference/shipments/list-shipments", + "api-docs/api-reference/shipments/get-a-shipment", + "api-docs/api-reference/shipments/edit-a-shipment", + "api-docs/api-reference/shipments/stop-tracking-shipment", + "api-docs/api-reference/shipments/resume-tracking-shipment" + ] + }, + { + "group": "Tracking Requests", + "pages": [ + "api-docs/api-reference/tracking-requests/list-tracking-requests", + "api-docs/api-reference/tracking-requests/create-a-tracking-request", + "api-docs/api-reference/tracking-requests/get-a-single-tracking-request", + "api-docs/api-reference/tracking-requests/edit-a-tracking-request", + "api-docs/improved/tracking-request-example" + ] + }, + { + "group": "Webhooks", + "pages": [ + "api-docs/api-reference/webhooks/get-single-webhook", + "api-docs/api-reference/webhooks/delete-a-webhook", + "api-docs/api-reference/webhooks/edit-a-webhook", + "api-docs/api-reference/webhooks/list-webhooks", + "api-docs/api-reference/webhooks/create-a-webhook", + "api-docs/api-reference/webhooks/list-webhook-ips" + ] + }, + { + "group": "Webhook Notifications", + "pages": [ + "api-docs/api-reference/webhook-notifications/get-a-single-webhook-notification", + "api-docs/api-reference/webhook-notifications/list-webhook-notifications", + "api-docs/api-reference/webhook-notifications/get-webhook-notification-payload-examples" + ] + }, + { + "group": "Containers", + "pages": [ + "api-docs/api-reference/containers/list-containers", + "api-docs/api-reference/containers/edit-a-container", + "api-docs/api-reference/containers/get-a-container", + "api-docs/api-reference/containers/get-a-containers-raw-events", + "api-docs/api-reference/containers/get-a-containers-transport-events" + ] + }, + { + "group": "Shipping Lines", + "pages": [ + "api-docs/api-reference/shipping-lines/shipping-lines", + "api-docs/api-reference/shipping-lines/get-a-single-shipping-line" + ] + }, + { + "group": "Metro Areas", + "pages": [ + "api-docs/api-reference/metro-areas/get-a-metro-area-using-the-unlocode-or-the-id" + ] + }, + { + "group": "Ports", + "pages": [ + "api-docs/api-reference/ports/get-a-port-using-the-locode-or-the-id" + ] + }, + { + "group": "Vessels", + "pages": [ + "api-docs/api-reference/vessels/get-a-vessel-using-the-id", + "api-docs/api-reference/vessels/get-a-vessel-using-the-imo" + ] + }, + { + "group": "Terminals", + "pages": [ + "api-docs/api-reference/terminals/get-a-terminal-using-the-id" + ] + }, + { + "group": "Parties", + "pages": [ + "api-docs/api-reference/parties/list-parties", + "api-docs/api-reference/parties/create-a-party", + "api-docs/api-reference/parties/get-a-party", + "api-docs/api-reference/parties/edit-a-party" + ] + } + ] + }, + { + "tab": "DataSync", + "groups": [ + { + "group": " ", + "pages": [ + "datasync/home" + ] + }, + { + "group": " ", + "pages": [ + "datasync/overview", + "datasync/supported-destinations" + ] + }, + { + "group": "Table Properties", + "pages": [ + "datasync/table-properties/containers_rail", + "datasync/table-properties/shipments", + "datasync/table-properties/tracking-requests", + "datasync/table-properties/transport-events", + "datasync/table-properties/transfer-status", + "datasync/table-properties/containers" + ] + } + ] + }, + { + "tab": "Data Sources", + "groups": [ + { + "group": "Overview", + "pages": [ + "api-docs/data-sources/overview", + "api-docs/useful-info/api-data-sources-availability" + ] + }, + { + "group": "Ocean Carriers", + "pages": [] + }, + { + "group": "Rail Carriers", + "pages": [] + }, + { + "group": "Terminals", + "pages": [] + } + ] + }, + { + "tab": "Embedding", + "groups": [ + { + "group": "Overview", + "pages": [ + "api-docs/embedding/overview" + ] + }, + { + "group": "Embeddable Map", + "pages": [ + "api-docs/in-depth-guides/terminal49-map" + ] + }, + { + "group": "Track & Trace Widget", + "pages": [ + "api-docs/in-depth-guides/terminal49-widget" + ] + } + ] + } + ], + "global": { + "anchors": [ + { + "anchor": "Contact", + "href": "https://www.terminal49.com/contact/", + "icon": "phone" + }, + { + "anchor": "Schedule Demo", + "href": "https://www.terminal49.com/demo/", + "icon": "calendar-days" + }, + { + "anchor": "Blog", + "href": "https://www.terminal49.com/blog/", + "icon": "newspaper" + } + ] + } + }, + "logo": "/logos/light.svg", + "appearance": { + "default": "light", + "strict": false + }, + "navbar": { + "links": [ + { + "label": "Start your free trial", + "href": "https://app.terminal49.com/register" + } + ], + "primary": { + "type": "button", + "label": "Sign in", + "href": "https://app.terminal49.com/" + } + }, + "footer": { + "socials": { + "twitter": "https://twitter.com/terminal49", + "linkedin": "https://www.linkedin.com/company/terminal49/" + } + }, + "redirects": [ + { + "source": "/api-docs/getting-started/recieve-status-updates", + "destination": "/api-docs/getting-started/receive-status-updates" + }, + { + "source": "/api-docs/getting-started/start-here", + "destination": "/api-docs/in-depth-guides/quickstart" + }, + { + "source": "/api-docs/in-depth-guides/api-quick-start", + "destination": "/api-docs/in-depth-guides/quickstart" + }, + { + "source": "/api-docs/getting-started/capabilities-overview", + "destination": "/api-docs/overview" + }, + { + "source": "/", + "destination": "/api-docs/overview" + }, + { + "source": "/home", + "destination": "/api-docs/overview" + } + ] +} diff --git a/docs/home.mdx b/docs/home.mdx deleted file mode 100644 index 186db702..00000000 --- a/docs/home.mdx +++ /dev/null @@ -1,35 +0,0 @@ ---- -title: Terminal49 Dev Documentation -og:title: Terminal49 Home | API Documentation -og:description: Learn about Terminal49's home features and API documentation for container and shipment tracking. ---- -We offer two fantastic ways to track your shipments from origin to destination. - - 1. [Terminal49 DataSync](/datasync/overview). Get tables full of fresh information delivered into your current data system. Easy to set up, and perfect for complementing your current data. - 2. [Terminal49 API](/api-docs/getting-started/start-here). Connect directly with the API, pull data for specific shipments and containers, and get updates via webhooks. - -If you already have a data store that feeds the rest of your system, DataSync is probably what you want. - -## What can I use Terminal49 data for? -Here are just a few of the data points we return and possible use-cases. - -| DATA | EXAMPLE USE CASE | -|----------------------------------------|--------------------------------------------------------------------------| -| Destination ETA | Surface ETA changes to your relevant teams as they're reported | -| Last Free Day and terminal status¹ | Track containers approaching LFD and prioritize dispatching | -| Fees and holds at destination terminal | Clear your cargo to keep you containers moving | -| Actual departure and arrival times | Report journey times by route to compare your ocean carriers performance | - -_1. At container ports in the US_ - -## How it works -All you need to provide are your BOL numbers and SCACs. Terminal 49 will lookup the shipment with the carrier and populate shipment details including containers. - -Once the shipment is set up, Terminal 49 periodically checks with the carrier and the destination terminal. - -If any of the details of your shipment or containers change (for example - if the ETA changes) we'll ensure you're always kept up to date. - - - If you're using DataSync, we'll update the data in your system - - If you're using the API, we'll post the shipment to the the webhook you provide - -👈🏽 Please click API Docs or Data Sync on the left to get started! \ No newline at end of file diff --git a/docs/images/disable-key.png b/docs/images/disable-key.png new file mode 100644 index 00000000..0519ecba --- /dev/null +++ b/docs/images/disable-key.png @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/mint.json b/docs/mint.json deleted file mode 100644 index 7af868bf..00000000 --- a/docs/mint.json +++ /dev/null @@ -1,210 +0,0 @@ -{ - "name": "Terminal 49", - "logo": "/logos/light.svg", - "favicon": "/logos/favicon.svg", - "colors": { - "primary": "#00A2FF", - "light": "#52bfff", - "dark": "#00A2FF" - }, - "modeToggle": { - "default": "light", - "isHidden": false - }, - "feedback": { - "thumbsRating": true, - "raiseIssue": true, - "suggestEdit": true - }, - "topbarCtaButton": { - "name": "Sign in", - "url": "https://app.terminal49.com/" - }, - "topbarLinks": [ - { - "name": "Start your free trial", - "url": "https://app.terminal49.com/register" - } - ], - "anchors": [ - { - "name": "Contact", - "icon": "phone", - "url": "https://www.terminal49.com/contact/" - }, - { - "name": "Schedule Demo", - "icon": "calendar-days", - "url": "https://www.terminal49.com/demo/" - }, - { - "name": "Blog", - "icon": "newspaper", - "url": "https://www.terminal49.com/blog/" - } - ], - "primaryTab": { - "name": "API Docs" - }, - "tabs": [ - { - "name": "DataSync", - "url": "datasync" - } - ], - "navigation": [ - { - "group": " ", - "pages": ["home"] - }, - { - "group": "Getting Started", - "pages": [ - "api-docs/getting-started/start-here", - "api-docs/getting-started/tracking-shipments-and-containers", - "api-docs/getting-started/list-shipments-and-containers", - "api-docs/getting-started/receive-status-updates" - ] - }, - { - "group": "In Depth Guides", - "pages": [ - "api-docs/in-depth-guides/quickstart", - "api-docs/in-depth-guides/webhooks", - "api-docs/in-depth-guides/including-resources", - "api-docs/in-depth-guides/adding-customer", - "api-docs/in-depth-guides/tracking-request-lifecycle", - "api-docs/in-depth-guides/event-timestamps", - "api-docs/in-depth-guides/terminal49-map", - "api-docs/in-depth-guides/terminal49-widget", - "api-docs/in-depth-guides/rail-integration-guide" - ] - }, - { - "group": "Useful Info", - "pages": [ - "api-docs/useful-info/api-data-sources-availability", - "api-docs/useful-info/pricing", - "api-docs/useful-info/test-numbers", - "api-docs/useful-info/tracking-request-retrying", - "api-docs/useful-info/webhook-events-examples" - ] - }, - { - "group": " ", - "pages": ["datasync/home"] - }, - { - "group": " ", - "pages": ["datasync/overview", "datasync/supported-destinations"] - }, - { - "group": "Table Properties", - "pages": [ - "datasync/table-properties/containers_rail", - "datasync/table-properties/shipments", - "datasync/table-properties/tracking-requests", - "datasync/table-properties/transport-events", - "datasync/table-properties/transfer-status", - "datasync/table-properties/containers" - ] - }, - { - "group": "Shipments", - "pages": [ - "api-docs/api-reference/shipments/list-shipments", - "api-docs/api-reference/shipments/get-a-shipment", - "api-docs/api-reference/shipments/edit-a-shipment", - "api-docs/api-reference/shipments/stop-tracking-shipment", - "api-docs/api-reference/shipments/resume-tracking-shipment" - ] - }, - { - "group": "Tracking Requests", - "pages": [ - "api-docs/api-reference/tracking-requests/list-tracking-requests", - "api-docs/api-reference/tracking-requests/create-a-tracking-request", - "api-docs/api-reference/tracking-requests/get-a-single-tracking-request", - "api-docs/api-reference/tracking-requests/edit-a-tracking-request" - ] - }, - { - "group": "Webhooks", - "pages": [ - "api-docs/api-reference/webhooks/get-single-webhook", - "api-docs/api-reference/webhooks/delete-a-webhook", - "api-docs/api-reference/webhooks/edit-a-webhook", - "api-docs/api-reference/webhooks/list-webhooks", - "api-docs/api-reference/webhooks/create-a-webhook", - "api-docs/api-reference/webhooks/list-webhook-ips" - ] - }, - { - "group": "Webhook Notifications", - "pages": [ - "api-docs/api-reference/webhook-notifications/get-a-single-webhook-notification", - "api-docs/api-reference/webhook-notifications/list-webhook-notifications", - "api-docs/api-reference/webhook-notifications/get-webhook-notification-payload-examples" - ] - }, - { - "group": "Containers", - "pages": [ - "api-docs/api-reference/containers/list-containers", - "api-docs/api-reference/containers/edit-a-container", - "api-docs/api-reference/containers/get-a-container", - "api-docs/api-reference/containers/get-a-containers-raw-events", - "api-docs/api-reference/containers/get-a-containers-transport-events" - ] - }, - { - "group": "Shipping Lines", - "pages": [ - "api-docs/api-reference/shipping-lines/shipping-lines", - "api-docs/api-reference/shipping-lines/get-a-single-shipping-line" - ] - }, - { - "group": "Metro Areas", - "pages": [ - "api-docs/api-reference/metro-areas/get-a-metro-area-using-the-unlocode-or-the-id" - ] - }, - { - "group": "Ports", - "pages": [ - "api-docs/api-reference/ports/get-a-port-using-the-locode-or-the-id" - ] - }, - { - "group": "Vessels", - "pages": [ - "api-docs/api-reference/vessels/get-a-vessel-using-the-id", - "api-docs/api-reference/vessels/get-a-vessel-using-the-imo" - ] - }, - { - "group": "Terminals", - "pages": ["api-docs/api-reference/terminals/get-a-terminal-using-the-id"] - }, - { - "group": "Parties", - "pages": [ - "api-docs/api-reference/parties/list-parties", - "api-docs/api-reference/parties/create-a-party", - "api-docs/api-reference/parties/get-a-party", - "api-docs/api-reference/parties/edit-a-party" - ] - } - ], - "footerSocials": { - "twitter": "https://twitter.com/terminal49", - "linkedin": "https://www.linkedin.com/company/terminal49/" - }, - "redirects": [ - { - "source": "/api-docs/getting-started/recieve-status-updates", - "destination": "/api-docs/getting-started/receive-status-updates" - } - ] -} diff --git a/docs/openapi.json.bak b/docs/openapi.json.bak new file mode 100644 index 00000000..6355a9ad --- /dev/null +++ b/docs/openapi.json.bak @@ -0,0 +1,15105 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Terminal49 API Reference", + "version": "0.2.0", + "contact": { + "name": "Terminal49 API support", + "url": "https://www.terminal49.com", + "email": "support@terminal49.com" + }, + "description": "The Terminal 49 API offers a convenient way to programmatically track your shipments from origin to destination.", + "x-label": "Beta", + "termsOfService": "https://www.terminal49.com/terms" + }, + "servers": [ + { + "url": "https://api.terminal49.com/v2", + "description": "Production" + } + ], + "paths": { + "/shipments": { + "get": { + "summary": "List shipments", + "tags": [ + "Shipments" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/shipment" + } + }, + "included": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/container" + }, + { + "$ref": "#/components/schemas/port" + }, + { + "$ref": "#/components/schemas/terminal" + } + ] + } + }, + "links": { + "$ref": "#/components/schemas/links" + }, + "meta": { + "$ref": "#/components/schemas/meta" + } + } + }, + "examples": { + "En-route to NY with inland move": { + "value": { + "data": [ + { + "id": "62738624-7032-4a50-892e-c55826228c25", + "type": "shipment", + "attributes": { + "created_at": "2024-06-26T17:28:59Z", + "ref_numbers": [], + "tags": [], + "bill_of_lading_number": "OOLU2148468620", + "normalized_number": "2148468620", + "shipping_line_scac": "OOLU", + "shipping_line_name": "Orient Overseas Container Line", + "shipping_line_short_name": "OOCL", + "customer_name": "Sodor Steamworks", + "port_of_lading_locode": "CNNBG", + "port_of_lading_name": "Ningbo", + "port_of_discharge_locode": "USLAX", + "port_of_discharge_name": "Los Angeles", + "pod_vessel_name": "EVER FORWARD", + "pod_vessel_imo": "9850551", + "pod_voyage_number": "1119E", + "destination_locode": "USCLE", + "destination_name": "Cleveland", + "destination_timezone": "America/New_York", + "destination_ata_at": null, + "destination_eta_at": "2024-07-02T08:00:00Z", + "pol_etd_at": null, + "pol_atd_at": "2024-06-09T03:42:00Z", + "pol_timezone": "Asia/Shanghai", + "pod_eta_at": null, + "pod_original_eta_at": null, + "pod_ata_at": "2024-06-22T13:36:00Z", + "pod_timezone": "America/Los_Angeles", + "line_tracking_last_attempted_at": "2024-06-26T17:28:59Z", + "line_tracking_last_succeeded_at": "2024-06-26T17:28:59Z", + "line_tracking_stopped_at": null, + "line_tracking_stopped_reason": null + }, + "links": { + "self": "/v2/shipments/62738624-7032-4a50-892e-c55826228c25" + }, + "relationships": { + "port_of_lading": { + "data": { + "id": "2a90c27b-c2ee-45e2-892f-5c695d74e2d0", + "type": "port" + } + }, + "port_of_discharge": { + "data": { + "id": "47b27584-4ec9-4e2f-95e1-7a42928cc40c", + "type": "port" + } + }, + "pod_terminal": { + "data": { + "id": "42c53b13-0d29-4fa6-8663-7343a56319f1", + "type": "terminal" + } + }, + "destination": { + "data": { + "id": "3b1cc325-ffe9-400a-82ce-f5c4891af382", + "type": "port" + } + }, + "destination_terminal": { + "data": { + "id": "ce22669e-14b2-4501-b782-f0a360f07cd0", + "type": "terminal" + } + }, + "line_tracking_stopped_by_user": { + "data": null + }, + "containers": { + "data": [ + { + "id": "7aefc29e-0898-4825-8376-4f998b51d033", + "type": "container" + } + ] + } + } + }, + { + "id": "baaa725e-aa0e-4937-ac78-54d9e2e8621e", + "type": "shipment", + "attributes": { + "created_at": "2024-06-26T16:47:42Z", + "ref_numbers": [], + "tags": [], + "bill_of_lading_number": "HDMUTAOM72244900", + "normalized_number": "TAOM72244900", + "shipping_line_scac": "HDMU", + "shipping_line_name": "Hyundai Merchant Marine", + "shipping_line_short_name": "Hyundai", + "customer_name": "Sodor Steamworks", + "port_of_lading_locode": "CNQDG", + "port_of_lading_name": "Qingdao", + "port_of_discharge_locode": "USSAV", + "port_of_discharge_name": "Savannah", + "pod_vessel_name": "UMM SALAL", + "pod_vessel_imo": "9525857", + "pod_voyage_number": "0038E", + "destination_locode": "USROQ", + "destination_name": "Rossville", + "destination_timezone": "America/Chicago", + "destination_ata_at": null, + "destination_eta_at": "2024-06-30T07:05:00Z", + "pol_etd_at": null, + "pol_atd_at": "2024-05-07T03:01:00Z", + "pol_timezone": "Asia/Shanghai", + "pod_eta_at": null, + "pod_original_eta_at": null, + "pod_ata_at": "2024-06-20T21:27:00Z", + "pod_timezone": "America/New_York", + "line_tracking_last_attempted_at": "2024-06-26T16:48:04Z", + "line_tracking_last_succeeded_at": "2024-06-26T16:48:04Z", + "line_tracking_stopped_at": null, + "line_tracking_stopped_reason": null + }, + "links": { + "self": "/v2/shipments/baaa725e-aa0e-4937-ac78-54d9e2e8621e" + }, + "relationships": { + "port_of_lading": { + "data": { + "id": "0ccbe8af-c8d0-4abd-a842-3bfad1d82024", + "type": "port" + } + }, + "port_of_discharge": { + "data": { + "id": "6129528d-846e-4571-ae16-b5328a4285ab", + "type": "port" + } + }, + "pod_terminal": { + "data": { + "id": "a243bdf8-0da3-4056-a6a7-05fe8ab43999", + "type": "terminal" + } + }, + "destination": { + "data": { + "id": "87ca3f37-e4d1-46eb-9eb1-6b5ffafde95d", + "type": "port" + } + }, + "destination_terminal": { + "data": null + }, + "line_tracking_stopped_by_user": { + "data": null + }, + "containers": { + "data": [ + { + "id": "772cd872-9677-4c68-9b7a-4e9e843b00e2", + "type": "container" + }, + { + "id": "52efc544-0de1-452f-bcf8-0290a6ce5c11", + "type": "container" + }, + { + "id": "3107692e-61ad-4b4c-b3d4-78348b2e37ff", + "type": "container" + } + ] + } + } + }, + { + "id": "7721a48c-5e93-43c9-9f5f-5be10a87fdde", + "type": "shipment", + "attributes": { + "created_at": "2024-06-26T16:28:39Z", + "ref_numbers": [], + "tags": [], + "bill_of_lading_number": "OOLU2738424980", + "normalized_number": "2738424980", + "shipping_line_scac": "OOLU", + "shipping_line_name": "Orient Overseas Container Line", + "shipping_line_short_name": "OOCL", + "customer_name": "Sodor Steamworks", + "port_of_lading_locode": "ITSPE", + "port_of_lading_name": "La Spezia", + "port_of_discharge_locode": "USSAV", + "port_of_discharge_name": "Savannah", + "pod_vessel_name": "OOCL GUANGZHOU", + "pod_vessel_imo": "9404869", + "pod_voyage_number": "162E", + "destination_locode": "USATL", + "destination_name": "Atlanta", + "destination_timezone": "America/New_York", + "destination_ata_at": null, + "destination_eta_at": "2024-06-27T06:42:00Z", + "pol_etd_at": null, + "pol_atd_at": "2024-06-05T08:03:00Z", + "pol_timezone": "Europe/Rome", + "pod_eta_at": null, + "pod_original_eta_at": null, + "pod_ata_at": "2024-06-23T15:34:00Z", + "pod_timezone": "America/New_York", + "line_tracking_last_attempted_at": "2024-06-26T16:28:39Z", + "line_tracking_last_succeeded_at": "2024-06-26T16:28:39Z", + "line_tracking_stopped_at": null, + "line_tracking_stopped_reason": null + }, + "links": { + "self": "/v2/shipments/7721a48c-5e93-43c9-9f5f-5be10a87fdde" + }, + "relationships": { + "port_of_lading": { + "data": { + "id": "b5656766-a56f-4b32-8e03-d240e7519604", + "type": "port" + } + }, + "port_of_discharge": { + "data": { + "id": "6129528d-846e-4571-ae16-b5328a4285ab", + "type": "port" + } + }, + "pod_terminal": { + "data": { + "id": "a243bdf8-0da3-4056-a6a7-05fe8ab43999", + "type": "terminal" + } + }, + "destination": { + "data": { + "id": "7daf9ea3-3018-4d62-b88c-43803df9030c", + "type": "port" + } + }, + "destination_terminal": { + "data": { + "id": "022ef8fc-1e2a-4ad6-8eae-330d65eb1c8e", + "type": "terminal" + } + }, + "line_tracking_stopped_by_user": { + "data": null + }, + "containers": { + "data": [ + { + "id": "2a25fd3e-a18e-47cc-9cea-62771e82d0f2", + "type": "container" + }, + { + "id": "6cdc725d-2b31-40f9-86dd-76225390a488", + "type": "container" + }, + { + "id": "2f1e9a9d-4689-4f4d-84a8-64409b56521d", + "type": "container" + } + ] + } + } + }, + { + "id": "32b5ad78-43ba-42d9-bdc0-4cf12320e020", + "type": "shipment", + "attributes": { + "created_at": "2024-06-26T15:59:52Z", + "ref_numbers": [], + "tags": [], + "bill_of_lading_number": "OOLU2738277190", + "normalized_number": "2738277190", + "shipping_line_scac": "OOLU", + "shipping_line_name": "Orient Overseas Container Line", + "shipping_line_short_name": "OOCL", + "customer_name": "Sodor Steamworks", + "port_of_lading_locode": "CNQDG", + "port_of_lading_name": "Qingdao", + "port_of_discharge_locode": "USLAX", + "port_of_discharge_name": "Los Angeles", + "pod_vessel_name": "EVER FORWARD", + "pod_vessel_imo": "9850551", + "pod_voyage_number": "1119E", + "destination_locode": "USMEM", + "destination_name": "Memphis", + "destination_timezone": "America/Chicago", + "destination_ata_at": null, + "destination_eta_at": "2024-07-01T14:00:00Z", + "pol_etd_at": null, + "pol_atd_at": "2024-06-02T18:22:00Z", + "pol_timezone": "Asia/Shanghai", + "pod_eta_at": null, + "pod_original_eta_at": null, + "pod_ata_at": "2024-06-22T13:36:00Z", + "pod_timezone": "America/Los_Angeles", + "line_tracking_last_attempted_at": "2024-06-26T15:59:52Z", + "line_tracking_last_succeeded_at": "2024-06-26T15:59:52Z", + "line_tracking_stopped_at": null, + "line_tracking_stopped_reason": null + }, + "links": { + "self": "/v2/shipments/32b5ad78-43ba-42d9-bdc0-4cf12320e020" + }, + "relationships": { + "port_of_lading": { + "data": { + "id": "0ccbe8af-c8d0-4abd-a842-3bfad1d82024", + "type": "port" + } + }, + "port_of_discharge": { + "data": { + "id": "47b27584-4ec9-4e2f-95e1-7a42928cc40c", + "type": "port" + } + }, + "pod_terminal": { + "data": { + "id": "42c53b13-0d29-4fa6-8663-7343a56319f1", + "type": "terminal" + } + }, + "destination": { + "data": { + "id": "ba0b9f68-2025-40ca-ae12-1c2210cca333", + "type": "port" + } + }, + "destination_terminal": { + "data": { + "id": "d0ec0da1-8a3a-4b11-b1e3-5716bbc71dc3", + "type": "terminal" + } + }, + "line_tracking_stopped_by_user": { + "data": null + }, + "containers": { + "data": [ + { + "id": "60d2fd62-14e2-4ca2-a927-9633fc58fdff", + "type": "container" + }, + { + "id": "0ffe3e75-b3df-4d20-b9f4-97bbbcec404d", + "type": "container" + } + ] + } + } + }, + { + "id": "bd117d3b-8fa4-487c-9bab-25c15e227d1a", + "type": "shipment", + "attributes": { + "created_at": "2024-06-26T15:59:35Z", + "ref_numbers": [], + "tags": [], + "bill_of_lading_number": "OOLU2147973020", + "normalized_number": "2147973020", + "shipping_line_scac": "OOLU", + "shipping_line_name": "Orient Overseas Container Line", + "shipping_line_short_name": "OOCL", + "customer_name": "Sodor Steamworks", + "port_of_lading_locode": "IDSUB", + "port_of_lading_name": "Surabaya", + "port_of_discharge_locode": "USLAX", + "port_of_discharge_name": "Los Angeles", + "pod_vessel_name": "CMA CGM A.LINCOLN", + "pod_vessel_imo": "9780859", + "pod_voyage_number": "1TU70E1MA", + "destination_locode": null, + "destination_name": null, + "destination_timezone": null, + "destination_ata_at": null, + "destination_eta_at": null, + "pol_etd_at": null, + "pol_atd_at": "2024-05-14T02:37:00Z", + "pol_timezone": "Asia/Jakarta", + "pod_eta_at": null, + "pod_original_eta_at": null, + "pod_ata_at": "2024-06-23T21:58:00Z", + "pod_timezone": "America/Los_Angeles", + "line_tracking_last_attempted_at": "2024-06-26T15:59:35Z", + "line_tracking_last_succeeded_at": "2024-06-26T15:59:35Z", + "line_tracking_stopped_at": null, + "line_tracking_stopped_reason": null + }, + "links": { + "self": "/v2/shipments/bd117d3b-8fa4-487c-9bab-25c15e227d1a" + }, + "relationships": { + "port_of_lading": { + "data": { + "id": "4201ab42-c51f-48ac-b7a1-12146e02c6a2", + "type": "port" + } + }, + "port_of_discharge": { + "data": { + "id": "47b27584-4ec9-4e2f-95e1-7a42928cc40c", + "type": "port" + } + }, + "pod_terminal": { + "data": { + "id": "eaa2580c-5f5b-4198-85e4-821145d62098", + "type": "terminal" + } + }, + "destination": { + "data": null + }, + "destination_terminal": { + "data": null + }, + "line_tracking_stopped_by_user": { + "data": null + }, + "containers": { + "data": [ + { + "id": "815ff702-fdb5-4455-a28e-314c345d7481", + "type": "container" + } + ] + } + } + } + ], + "meta": { + "size": 5, + "total": 34044 + }, + "links": { + "self": "https://api.terminal49.com/v2/shipments?page[size]=5", + "current": "https://api.terminal49.com/v2/shipments?page[number]=1&page[size]=5", + "next": "https://api.terminal49.com/v2/shipments?page[number]=2&page[size]=5", + "last": "https://api.terminal49.com/v2/shipments?page[number]=6809&page[size]=5" + } + } + } + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "errors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "examples": { + "Errors": { + "value": { + "error": [ + { + "title": "Invalid arrival_date_from", + "detail": "filter['arrival_from'] must be in the format 'YYYY-MM-DD' and a valid date" + }, + { + "title": "Invalid arrival_date_to", + "detail": "filter['arrival_from'] must be in the format 'YYYY-MM-DD' and a valid date" + }, + { + "title": "Invalid port_of_lading", + "detail": "port_of_discharge must be an array of 5 character UN/LOCODEs" + }, + { + "title": "Invalid port_of_discharge", + "detail": "port_of_discharge must be an array of 5 character UN/LOCODEs" + } + ] + } + } + } + } + } + } + }, + "operationId": "get-shipments", + "description": "Returns a list of your shipments. The shipments are returned sorted by creation date, with the most recent shipments appearing first.\n\nThis api will return all shipments associated with the account. Shipments created via the `tracking_request` API aswell as the ones added via the dashboard will be retuned via this endpoint. ", + "parameters": [ + { + "schema": { + "type": "integer", + "default": 1 + }, + "in": "query", + "name": "page[number]", + "description": "\n" + }, + { + "schema": { + "type": "integer", + "default": 30 + }, + "in": "query", + "name": "page[size]", + "description": "\n" + }, + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "q", + "description": "\nSearch shipments by master bill of lading, reference number, or container number.", + "deprecated": true + }, + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "include", + "description": "Comma delimited list of relations to include" + }, + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "number", + "description": "Search shipments by the original request tracking `request_number`" + } + ] + } + }, + "/shipments/{id}": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "id", + "in": "path", + "required": true, + "description": "Shipment Id" + } + ], + "get": { + "summary": "Get a shipment", + "tags": [ + "Shipments" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/shipment" + }, + "included": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/container" + }, + { + "$ref": "#/components/schemas/port" + }, + { + "$ref": "#/components/schemas/terminal" + } + ] + } + } + } + }, + "examples": { + "En-route to NY with inland move": { + "value": { + "data": { + "id": "02b1bd6f-407c-45bb-8645-06e7ee34e7e3", + "type": "shipment", + "attributes": { + "created_at": "2024-06-26T15:05:20Z", + "ref_numbers": [], + "tags": [], + "bill_of_lading_number": "MEDUF5399896", + "normalized_number": "MEDUF5399896", + "shipping_line_scac": "MSCU", + "shipping_line_name": "Mediterranean Shipping Company", + "shipping_line_short_name": "MSC", + "customer_name": "Sodor Steamworks", + "port_of_lading_locode": "FRLEH", + "port_of_lading_name": "Le Havre", + "port_of_discharge_locode": "USNYC", + "port_of_discharge_name": "New York / New Jersey", + "pod_vessel_name": "MSC ANISHA R.", + "pod_vessel_imo": "9227297", + "pod_voyage_number": "421A", + "destination_locode": "USIND", + "destination_name": "Indianapolis", + "destination_timezone": "US/Eastern", + "destination_ata_at": null, + "destination_eta_at": null, + "pol_etd_at": null, + "pol_atd_at": "2024-06-11T22:00:00Z", + "pol_timezone": "Europe/Paris", + "pod_eta_at": null, + "pod_original_eta_at": null, + "pod_ata_at": null, + "pod_timezone": "America/New_York", + "line_tracking_last_attempted_at": "2024-06-26T15:05:20Z", + "line_tracking_last_succeeded_at": "2024-06-26T15:05:20Z", + "line_tracking_stopped_at": null, + "line_tracking_stopped_reason": null + }, + "links": { + "self": "/v2/shipments/02b1bd6f-407c-45bb-8645-06e7ee34e7e3" + }, + "relationships": { + "port_of_lading": { + "data": { + "id": "fe7fc831-8a81-4079-8938-b90015912e8b", + "type": "port" + } + }, + "port_of_discharge": { + "data": { + "id": "f7cb530a-9e60-412c-a5bc-205a2f34ba54", + "type": "port" + } + }, + "pod_terminal": { + "data": { + "id": "b859f5c3-8515-41da-bf20-39c0a5ada887", + "type": "terminal" + } + }, + "destination": { + "data": { + "id": "e1e492f6-c8ba-45a9-ad1a-67a7e74547ce", + "type": "port" + } + }, + "destination_terminal": { + "data": null + }, + "line_tracking_stopped_by_user": { + "data": null + }, + "containers": { + "data": [ + { + "id": "55a700e4-7005-45a9-92fd-1ff38641dbd9", + "type": "container" + } + ] + } + } + }, + "links": { + "self": "https://api.terminal49.com/v2/shipments/02b1bd6f-407c-45bb-8645-06e7ee34e7e3?include=containers" + }, + "included": [ + { + "id": "55a700e4-7005-45a9-92fd-1ff38641dbd9", + "type": "container", + "attributes": { + "number": "CAIU7432986", + "seal_number": null, + "created_at": "2024-06-26T15:05:21Z", + "ref_numbers": [], + "pod_arrived_at": null, + "pod_discharged_at": "2024-06-22T04:00:00Z", + "final_destination_full_out_at": null, + "holds_at_pod_terminal": [], + "available_for_pickup": true, + "equipment_type": "dry", + "equipment_length": 40, + "equipment_height": "high_cube", + "weight_in_lbs": null, + "pod_full_out_at": null, + "empty_terminated_at": null, + "terminal_checked_at": "2024-06-26T17:51:12Z", + "fees_at_pod_terminal": [], + "pickup_lfd": "2024-07-07T04:00:00Z", + "pickup_appointment_at": null, + "pod_full_out_chassis_number": null, + "location_at_pod_terminal": "Yard - Y0709A", + "pod_last_tracking_request_at": "2024-06-26T17:51:12Z", + "shipment_last_tracking_request_at": "2024-06-26T15:05:20Z", + "availability_known": true, + "pod_timezone": "America/New_York", + "final_destination_timezone": "US/Eastern", + "empty_terminated_timezone": "US/Eastern", + "pod_rail_carrier_scac": "CSXT", + "ind_rail_carrier_scac": "CSXT", + "pod_rail_loaded_at": null, + "pod_rail_departed_at": null, + "ind_eta_at": null, + "ind_ata_at": null, + "ind_rail_unloaded_at": null, + "ind_facility_lfd_on": null + }, + "relationships": { + "shipment": { + "data": { + "id": "02b1bd6f-407c-45bb-8645-06e7ee34e7e3", + "type": "shipment" + } + }, + "pickup_facility": { + "data": null + }, + "pod_terminal": { + "data": { + "id": "b859f5c3-8515-41da-bf20-39c0a5ada887", + "type": "terminal" + } + }, + "transport_events": { + "data": [ + { + "id": "45b542cb-332b-4684-b915-42e3a0759823", + "type": "transport_event" + }, + { + "id": "174ed528-a1a9-4002-aef0-f2c9369199da", + "type": "transport_event" + }, + { + "id": "7a2f30a6-a756-4c14-9477-fbfc1c7fe2f8", + "type": "transport_event" + }, + { + "id": "e7365004-175a-46e8-96cd-dbed0f3daf21", + "type": "transport_event" + }, + { + "id": "7c567bf3-7f01-4a3d-a176-eaa1f7165585", + "type": "transport_event" + } + ] + }, + "raw_events": { + "data": [ + { + "id": "2956f71c-bfb9-4e49-b9e2-1b4d53c74cac", + "type": "raw_event" + }, + { + "id": "391e0eda-65b5-4fc3-a53d-25ecd9570259", + "type": "raw_event" + }, + { + "id": "74810c04-6c8a-4194-8cff-52936584a965", + "type": "raw_event" + }, + { + "id": "4b1500e2-b23b-4896-87bd-c38b1d16f385", + "type": "raw_event" + }, + { + "id": "8b9a7d88-720a-4304-8c1e-a3336e39f481", + "type": "raw_event" + }, + { + "id": "bf1f59c5-5dd8-4013-87f9-d7056bc87114", + "type": "raw_event" + } + ] + } + } + } + ] + } + } + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "errors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "examples": { + "En-route to NY with inland move": { + "value": { + "data": { + "id": "512ae32c-a604-44f8-9c15-2ca9cd3d2ae8", + "type": "shipment", + "attributes": { + "bill_of_lading_number": "6140575020", + "ref_numbers": [ + "my-ref-49", + "PO#18412" + ], + "created_at": "2020-02-20T13:37:12Z", + "tags": [], + "port_of_lading_locode": "INVTZ", + "port_of_lading_name": "Visakhapatnam, IN", + "port_of_discharge_locode": "USNYC", + "port_of_discharge_name": "New York / New Jersey, NY", + "destination_locode": "USDET", + "destination_name": "Detroit, MI", + "shipping_line_scac": "HLCU", + "pod_vessel_name": "CMA CGM ALMAVIVA", + "pod_voyage_number": "0108", + "pol_etd_at": null, + "pol_atd_at": "2020-02-15T21:53Z", + "pol_timezone": "Asia/Calcutta", + "pod_eta_at": "2020-03-18T08:00Z", + "pod_ata_at": null, + "pod_timezone": "America/New_York", + "destination_eta_at": "2020-03-26T17:00Z", + "destination_ata_at": null, + "destination_timezone": "America/Detroit" + }, + "relationships": { + "containers": { + "data": [ + { + "id": "c99a81c0-ff69-4bdf-aa5f-8e33a787f5fa", + "type": "container" + } + ] + }, + "port_of_lading": { + "data": { + "id": "bde5465a-1160-4fde-a026-74df9c362f65", + "type": "port" + } + }, + "port_of_discharge": { + "data": { + "id": "f7cb530a-9e60-412c-a5bc-205a2f34ba54", + "type": "port" + } + }, + "destination": { + "data": { + "id": "c9ae2b6b-5088-4e07-ba09-2872121e4fa2", + "type": "metro_area" + } + }, + "customer": { + "data": { + "id": "ff76b51a-371e-45ec-86d1-9d03ccae566a", + "type": "account" + } + } + } + }, + "included": [ + { + "id": "c99a81c0-ff69-4bdf-aa5f-8e33a787f5fa", + "type": "container", + "attributes": { + "number": "UACU4743531", + "equipment_type": "reefer", + "length": 40, + "height": "high_cube", + "weight_in_lbs": 35075, + "created_at": "2020-02-20T08:19:52Z", + "seal_number": null, + "pickup_lfd": null, + "availability_known": false, + "available_for_pickup": null, + "current_transportation_mode": "vessel", + "pod_discharged_at": null, + "pod_picked_up_at": null, + "destination_unloaded_at": null, + "destination_picked_up_at": null, + "empty_returned_at": null, + "pod_timezone": "America/Los_Angeles", + "final_destination_timezone": null, + "empty_terminated_timezone": null + }, + "relationships": { + "most_recent_location": { + "data": { + "id": "dd179094-a1d4-4129-842d-b952e43df4b7", + "type": "port" + } + }, + "shipment": { + "data": { + "id": "512ae32c-a604-44f8-9c15-2ca9cd3d2ae8", + "type": "shipment" + } + } + } + }, + { + "id": "bde5465a-1160-4fde-a026-74df9c362f65", + "type": "port", + "attributes": { + "name": "Visakhapatnam", + "code": "INVTZ", + "country_code": "IN", + "timezone": "Asia/Calcutta" + } + }, + { + "id": "dd179094-a1d4-4129-842d-b952e43df4b7", + "type": "port", + "attributes": { + "name": "Damietta", + "code": "EGDAM", + "country_code": "EG", + "time_zone": "Africa/Cairo" + } + }, + { + "id": "f7cb530a-9e60-412c-a5bc-205a2f34ba54", + "type": "port", + "attributes": { + "name": "New York / New Jersey", + "code": "USNYC", + "country_code": "US", + "timezone": "America/New_York" + } + }, + { + "id": "c9ae2b6b-5088-4e07-ba09-2872121e4fa2", + "type": "metro_area", + "attributes": { + "name": "Detroit", + "code": "USDET", + "country_code": "US", + "state_abbr": "MI", + "timezone": "America/Detroit" + } + }, + { + "id": "ff76b51a-371e-45ec-86d1-9d03ccae566a", + "type": "account", + "attributes": { + "name": "A-Z Imports" + } + }, + { + "id": "252a5450-2893-4207-b5c4-81ce3152ce84", + "type": "vessel", + "attributes": { + "name": "CMA CGM ALMAVIVA", + "imo": "9450648", + "mmsi": "228339600", + "latitude": 70.22625823437389, + "longitude": 45.06279126658865, + "nautical_speed_knots": 100, + "navigational_heading_degrees": 1, + "position_timestamp": "2023-06-05T19:46:18Z" + } + } + ] + } + } + } + } + } + } + }, + "operationId": "get-shipment-id", + "description": "Retrieves the details of an existing shipment. You need only supply the unique shipment `id` that was returned upon `tracking_request` creation.", + "parameters": [ + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "include", + "description": "Comma delimited list of relations to include" + } + ] + }, + "patch": { + "summary": "Edit a shipment", + "operationId": "patch-shipments-id", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/shipment" + } + } + } + } + } + } + }, + "description": "Update a shipment", + "tags": [ + "Shipments" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "required": [ + "attributes" + ], + "properties": { + "attributes": { + "type": "object", + "properties": { + "ref_numbers": { + "type": "array", + "example": [ + "REFNUMBER10" + ], + "description": "Shipment ref numbers.", + "items": { + "type": "string" + } + }, + "shipment_tags": { + "type": "array", + "x-stoplight": { + "id": "02itol38fmg55" + }, + "uniqueItems": true, + "description": "Tags related to a shipment", + "items": { + "x-stoplight": { + "id": "64x90wvr9d6nd" + }, + "type": "string", + "example": "tag1, tag2" + } + } + } + } + } + } + } + } + } + } + } + } + }, + "/shipments/{id}/stop_tracking": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "id", + "in": "path", + "required": true + } + ], + "patch": { + "summary": "Stop tracking a shipment", + "operationId": "patch-shipments-id-stop-tracking", + "tags": [ + "Shipments" + ], + "description": "We'll stop tracking the shipment, which means that there will be no more updates. You can still access the shipment's previously-collected information via the API or dashboard.\n\nYou can resume tracking a shipment by calling the `resume_tracking` endpoint, but keep in mind that some information is only made available by our data sources at specific times, so a stopped and resumed shipment may have some information missing.", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/shipment" + } + } + } + } + } + } + } + } + }, + "/shipments/{id}/resume_tracking": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "id", + "in": "path", + "required": true + } + ], + "patch": { + "summary": "Resume tracking a shipment", + "operationId": "patch-shipments-id-resume-tracking", + "tags": [ + "Shipments" + ], + "description": "Resume tracking a shipment. Keep in mind that some information is only made available by our data sources at specific times, so a stopped and resumed shipment may have some information missing.", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/shipment" + } + } + } + } + } + } + } + } + }, + "/tracking_requests": { + "post": { + "summary": "Create a tracking request", + "operationId": "post-track", + "responses": { + "201": { + "description": "Tracking Request Created", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/tracking_request" + }, + "included": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/account" + }, + { + "$ref": "#/components/schemas/shipping_line" + } + ] + } + } + } + }, + "examples": { + "Pending Tracking Request": { + "value": { + "data": { + "id": "ba4cb904-827f-4038-8e31-1e92b3356218", + "type": "tracking_request", + "attributes": { + "request_number": "MEDUFR030802", + "request_type": "bill_of_lading", + "scac": "MSCU", + "ref_numbers": [], + "created_at": "2020-04-04T16:13:35-07:00", + "updated_at": "2020-04-04T17:13:35-07:00", + "status": "pending", + "failed_reason": null + }, + "relationships": { + "tracked_object": { + "data": null + }, + "customer": { + "data": { + "id": "f7cb530a-9e60-412c-a5bc-205a2f34ba54", + "type": "party" + } + } + }, + "links": { + "self": "/v2/tracking_requests/ba4cb904-827f-4038-8e31-1e92b3356218" + } + } + } + }, + "Example: MSC BL": { + "summary": "Track by Bill of Lading", + "value": { + "data": { + "attributes": { + "request_type": "bill_of_lading", + "request_number": "MEDUFR030802", + "ref_numbers": [ + "PO12345", + "HBL12345", + "CUSREF1234" + ], + "shipment_tags": [ + "camembert" + ], + "scac": "MSCU" + }, + "relationships": { + "customer": { + "data": { + "id": "f7cb530a-9e60-412c-a5bc-205a2f34ba54", + "type": "party" + } + } + }, + "type": "tracking_request" + } + } + }, + "Example: Booking Number": { + "summary": "Track by Booking Number with References, Tags and Customer", + "value": { + "data": { + "attributes": { + "request_type": "booking_number", + "request_number": "OOLU8324567", + "ref_numbers": [ + "PO-8765", + "INV-12345" + ], + "shipment_tags": [ + "priority", + "electronics" + ], + "scac": "OOLU" + }, + "relationships": { + "customer": { + "data": { + "id": "a9e52f3d-2fa9-467c-8deb-09e90dac2f0b", + "type": "party" + } + } + }, + "type": "tracking_request" + } + } + }, + "Example: Container": { + "value": { + "data": { + "type": "tracking_request", + "attributes": { + "request_type": "container", + "request_number": "MSCU1234567", + "scac": "MSCU" + } + } + }, + "summary": "Track by Container Number" + }, + "Example: Test Number": { + "value": { + "data": { + "type": "tracking_request", + "attributes": { + "request_type": "bill_of_lading", + "request_number": "TEST-TR-SUCCEEDED", + "scac": "TEST" + } + } + }, + "summary": "Using Test Numbers for Development" + } + } + } + }, + "headers": {} + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "errors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "examples": { + "Error Examples": { + "value": { + "errors": [ + { + "status": "422", + "source": { + "pointer": "/data/attributes/scac" + }, + "title": "Unprocessable Entity", + "detail": "Scac can't be blank", + "code": "blank" + }, + { + "status": "422", + "source": { + "pointer": "/data/attributes/scac" + }, + "title": "Unprocessable Entity", + "detail": "Scac 'XXXX' is not recognized", + "code": "blank" + }, + { + "status": "422", + "source": { + "pointer": "/data/attributes/scac" + }, + "title": "Unprocessable Entity", + "detail": "Scac 'UALC' is not supported. We do not currently integrate with Universal Africa Lines", + "code": "blank" + }, + { + "status": "422", + "source": { + "pointer": "/data/attributes/request_number" + }, + "title": "Unprocessable Entity", + "detail": "Request number can't be blank", + "code": "blank" + } + ] + } + } + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "properties": { + "request_type": { + "type": "string", + "example": "bill_of_lading", + "enum": [ + "bill_of_lading", + "booking_number", + "container" + ], + "description": " The type of document number to be supplied. Container number support is currently in BETA." + }, + "request_number": { + "type": "string", + "example": "MEDUFR030802" + }, + "scac": { + "type": "string", + "example": "MSCU", + "minLength": 4, + "maxLength": 4 + }, + "ref_numbers": { + "type": "array", + "description": "Optional list of reference numbers to be added to the shipment when tracking request completes", + "items": { + "type": "string" + } + }, + "shipment_tags": { + "type": "array", + "description": "Optional list of tags to be added to the shipment when tracking request completes", + "items": { + "type": "string" + } + } + }, + "required": [ + "request_type", + "request_number", + "scac" + ] + }, + "relationships": { + "type": "object", + "properties": { + "customer": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "party" + ] + } + } + } + } + } + } + }, + "type": { + "enum": [ + "tracking_request" + ], + "type": "string" + } + }, + "required": [ + "type" + ] + } + } + }, + "examples": { + "Example: MSC BL": { + "value": { + "data": { + "attributes": { + "request_type": "bill_of_lading", + "request_number": "MEDUFR030802", + "ref_numbers": [ + "PO12345", + "HBL12345", + "CUSREF1234" + ], + "shipment_tags": [ + "camembert" + ], + "scac": "MSCU" + }, + "relationships": { + "customer": { + "data": { + "id": "f7cb530a-9e60-412c-a5bc-205a2f34ba54", + "type": "party" + } + } + }, + "type": "tracking_request" + } + } + }, + "Example: Booking Number": { + "value": { + "data": { + "attributes": { + "request_type": "booking_number", + "request_number": "OOLU8324567", + "ref_numbers": [ + "PO-8765", + "INV-12345" + ], + "shipment_tags": [ + "priority", + "electronics" + ], + "scac": "OOLU" + }, + "relationships": { + "customer": { + "data": { + "id": "a9e52f3d-2fa9-467c-8deb-09e90dac2f0b", + "type": "party" + } + } + }, + "type": "tracking_request" + } + } + }, + "Example: Container": { + "value": { + "data": { + "type": "tracking_request", + "attributes": { + "request_type": "container", + "request_number": "MSCU1234567", + "scac": "MSCU" + } + } + } + }, + "Example: Test Number": { + "value": { + "data": { + "type": "tracking_request", + "attributes": { + "request_type": "bill_of_lading", + "request_number": "TEST-TR-SUCCEEDED", + "scac": "TEST" + } + } + } + } + } + } + }, + "description": "Create a shipment tracking request" + }, + "security": [ + { + "authorization": [] + } + ], + "description": "To track an ocean shipment, you create a new tracking request. \nTwo attributes are required to track a shipment. A `bill of lading/booking number` and a shipping line `SCAC`. \n\nOnce a tracking request is created we will attempt to fetch the shipment details and it's related containers from the shipping line. If the attempt is successful we will create in new shipment object including any related container objects. We will send a `tracking_request.succeeded` webhook notification to your webhooks. \n\nIf the attempt to fetch fails then we will send a `tracking_request.failed` webhook notification to your `webhooks`. \n\nA `tracking_request.succeeded` or `tracking_request.failed` webhook notificaiton will only be sent if you have atleast one active webhook. ", + "tags": [ + "Tracking Requests" + ], + "parameters": [] + }, + "parameters": [], + "get": { + "summary": "List tracking requests", + "operationId": "get-tracking-requests", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/tracking_request" + } + }, + "links": { + "$ref": "#/components/schemas/links" + }, + "meta": { + "$ref": "#/components/schemas/meta" + }, + "included": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/account" + }, + { + "$ref": "#/components/schemas/shipping_line" + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "shipment" + ] + }, + "links": { + "type": "object", + "properties": { + "self": { + "type": "string", + "format": "uri" + } + } + } + }, + "description": "" + } + ] + } + } + } + }, + "examples": {} + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "errors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/error" + } + } + } + } + } + } + } + }, + "description": "Returns a list of your tracking requests. The tracking requests are returned sorted by creation date, with the most recent tracking request appearing first.", + "tags": [ + "Tracking Requests" + ], + "parameters": [ + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "q", + "description": "A search term to be applied against request_number and reference_numbers.", + "deprecated": true + }, + { + "schema": { + "type": "string", + "enum": [ + "created", + "pending", + "failed" + ], + "example": "created" + }, + "in": "query", + "name": "filter[status]", + "description": "filter by `status`" + }, + { + "schema": { + "type": "string", + "example": "MSCU" + }, + "in": "query", + "name": "filter[scac]", + "description": "filter by shipping line `scac`" + }, + { + "schema": { + "type": "string", + "example": "2020-04-28T22:59:15Z", + "format": "date-time" + }, + "in": "query", + "name": "filter[created_at][start]", + "description": "filter by tracking_requests `created_at` after a certain ISO8601 timestamp" + }, + { + "schema": { + "type": "string", + "format": "date-time", + "example": "2020-04-28T22:59:15Z" + }, + "in": "query", + "description": "filter by tracking_requests `created_at` before a certain ISO8601 timestamp", + "name": "filter[created_at][end]" + }, + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "include", + "description": "Comma delimited list of relations to include. 'tracked_object' is included by default." + }, + { + "schema": { + "type": "integer" + }, + "in": "query", + "name": "page[number]" + }, + { + "schema": { + "type": "integer" + }, + "in": "query", + "name": "page[size]" + }, + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "filter[request_number]", + "description": "filter by `request_number`" + } + ] + } + }, + "/tracking_requests/{id}": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "id", + "in": "path", + "required": true, + "description": "Tracking Request ID" + } + ], + "get": { + "summary": "Get a single tracking request", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/tracking_request" + }, + "included": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/account" + }, + { + "$ref": "#/components/schemas/shipment" + }, + { + "$ref": "#/components/schemas/shipping_line" + } + ] + } + } + } + }, + "examples": { + "With Status Created": { + "value": { + "data": { + "id": "ba4cb904-827f-4038-8e31-1e92b3356218", + "type": "tracking_request", + "attributes": { + "request_number": "MEDUFR030802", + "request_type": "bill_of_lading", + "scac": "MSCU", + "ref_numbers": [], + "created_at": "2020-04-04T16:13:35-07:00", + "updated_at": "2020-04-04T17:13:35-07:00", + "status": "created", + "failed_reason": null + }, + "relationships": { + "tracked_object": { + "data": { + "id": "eb6f218a-0b4a-47f9-8ef9-759aa5e0ea83", + "type": "shipment" + } + } + }, + "links": { + "self": "/v2/tracking_requests/ba4cb904-827f-4038-8e31-1e92b3356218" + } + }, + "included": [ + { + "id": "eb6f218a-0b4a-47f9-8ef9-759aa5e0ea83", + "type": "shipment", + "attributes": { + "created_at": "2020-04-04T16:13:37-07:00", + "bill_of_lading_number": "MEDUFR030802", + "ref_numbers": [], + "shipping_line_scac": "MSCU", + "shipping_line_name": "Mediterranean Shipping Company", + "port_of_lading_locode": "FRFOS", + "port_of_lading_name": "Fos-Sur-Mer", + "port_of_discharge_locode": "USOAK", + "port_of_discharge_name": "Oakland", + "pod_vessel_name": "MSC ALGECIRAS", + "pod_vessel_imo": "9605243", + "pod_voyage_number": "920A", + "destination_locode": "USOAK", + "destination_name": "Oakland", + "destination_timezone": "America/Los_Angeles", + "destination_ata_at": "2019-06-21T18:46:00-07:00", + "destination_eta_at": null, + "pol_etd_at": null, + "pol_atd_at": "2019-05-24T03:00:00-07:00", + "pol_timezone": "Europe/Paris", + "pod_eta_at": null, + "pod_ata_at": "2019-06-21T18:46:00-07:00", + "pod_timezone": "America/Los_Angeles" + }, + "relationships": { + "port_of_lading": { + "data": { + "id": "6d8c6c29-72a6-49ad-87b7-fd045f202212", + "type": "port" + } + }, + "port_of_discharge": { + "data": { + "id": "42d1ba3a-f4b8-431d-a6fe-49fd748a59e7", + "type": "port" + } + }, + "pod_terminal": { + "data": null + }, + "destination": { + "data": { + "id": "42d1ba3a-f4b8-431d-a6fe-49fd748a59e7", + "type": "port" + } + }, + "containers": { + "data": [ + { + "id": "11c1fa10-52a5-48e2-82f4-5523756b3d0f", + "type": "container" + } + ] + } + }, + "links": { + "self": "/v2/shipments/eb6f218a-0b4a-47f9-8ef9-759aa5e0ea83" + } + } + ] + } + }, + "Multiple containers": { + "value": { + "data": { + "id": "62c30bd4-d7fc-40dc-9fd6-fb39224301f5", + "type": "tracking_request", + "attributes": { + "request_number": "212157148", + "request_type": "bill_of_lading", + "scac": "MAEU", + "ref_numbers": [], + "shipment_tags": [], + "created_at": "2021-07-27T16:44:14Z", + "updated_at": "2021-07-27T17:44:14Z", + "status": "created", + "failed_reason": null, + "is_retrying": false, + "retry_count": null + }, + "relationships": { + "tracked_object": { + "data": { + "id": "dfc9f601-f6fe-412e-a71c-feabcc2dd4e3", + "type": "shipment" + } + }, + "customer": { + "data": null + } + }, + "links": { + "self": "/v2/tracking_requests/62c30bd4-d7fc-40dc-9fd6-fb39224301f5" + } + }, + "links": { + "self": "https://api.terminal49.com/v2/tracking_requests/62c30bd4-d7fc-40dc-9fd6-fb39224301f5?filter%5Bstatus%5D=created" + }, + "included": [ + { + "id": "dfc9f601-f6fe-412e-a71c-feabcc2dd4e3", + "type": "shipment", + "attributes": { + "created_at": "2021-07-27T16:44:16Z", + "ref_numbers": null, + "tags": [], + "bill_of_lading_number": "212157148", + "shipping_line_scac": "MAEU", + "shipping_line_name": "Maersk", + "shipping_line_short_name": "Maersk", + "port_of_lading_locode": "MYTPP", + "port_of_lading_name": "Tanjung Pelepas", + "port_of_discharge_locode": null, + "port_of_discharge_name": null, + "pod_vessel_name": null, + "pod_vessel_imo": null, + "pod_voyage_number": null, + "destination_locode": null, + "destination_name": null, + "destination_timezone": null, + "destination_ata_at": null, + "destination_eta_at": null, + "pol_etd_at": null, + "pol_atd_at": null, + "pol_timezone": "Asia/Kuala_Lumpur", + "pod_eta_at": "2021-09-15T15:00:00Z", + "pod_ata_at": null, + "pod_timezone": null, + "line_tracking_last_attempted_at": null, + "line_tracking_last_succeeded_at": "2021-07-27T16:44:16Z", + "line_tracking_stopped_at": null, + "line_tracking_stopped_reason": null + }, + "relationships": { + "port_of_lading": { + "data": { + "id": "6c387786-252c-476d-9f99-7d835b6b978b", + "type": "port" + } + }, + "port_of_discharge": { + "data": null + }, + "pod_terminal": { + "data": null + }, + "destination": { + "data": null + }, + "destination_terminal": { + "data": null + }, + "containers": { + "data": [ + { + "id": "965880c9-a37e-4ed7-a060-9c49c0f0c5ed", + "type": "container" + }, + { + "id": "ea1f8e08-fcdf-498d-9cb5-0c370b023eeb", + "type": "container" + }, + { + "id": "67f55105-8ea2-4137-9244-f9cc204f5766", + "type": "container" + }, + { + "id": "5ab5d058-772c-466c-bc73-0b8767ad5a79", + "type": "container" + } + ] + } + }, + "links": { + "self": "/v2/shipments/dfc9f601-f6fe-412e-a71c-feabcc2dd4e3" + } + } + ] + } + } + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "errors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/error" + } + } + } + } + } + } + } + }, + "operationId": "get-track-request-by-id", + "description": "Get the details and status of an existing tracking request. ", + "tags": [ + "Tracking Requests" + ], + "parameters": [ + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "include", + "description": "Comma delimited list of relations to include. 'tracked_object' is included by default." + } + ], + "x-internal": false + }, + "patch": { + "summary": "Edit a tracking request", + "operationId": "patch-track-request-by-id", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/tracking_request" + } + } + } + } + } + } + }, + "description": "Update a tracking request", + "tags": [ + "Tracking Requests" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "type": "object", + "properties": { + "ref_number": { + "type": "string", + "example": "REFNUMBER11", + "description": "Tracking request ref number." + } + } + } + } + } + } + } + } + } + } + } + }, + "/webhooks/{id}": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "id", + "in": "path", + "required": true + } + ], + "get": { + "summary": "Get single webhook", + "tags": [ + "Webhooks" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/webhook" + } + } + } + } + } + } + }, + "operationId": "get-webhooks-id", + "description": "Get the details of a single webhook" + }, + "patch": { + "summary": "Edit a webhook", + "operationId": "patch-webhooks-id", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/webhook" + } + } + } + } + } + } + }, + "description": "Update a single webhook", + "tags": [ + "Webhooks" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "type": "object", + "properties": { + "url": { + "type": "string", + "example": "https://webhook.site/#!/39084fbb-d887-42e8-be08-b9183ad02362", + "format": "uri", + "description": "The URL of the webhook endpoint." + }, + "events": { + "type": "array", + "description": "The list of events to enable for this endpoint.", + "uniqueItems": true, + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "container.transport.vessel_arrived", + "container.transport.vessel_discharged", + "container.transport.vessel_loaded", + "container.transport.vessel_departed", + "container.transport.rail_departed", + "container.transport.rail_arrived", + "container.transport.rail_loaded", + "container.transport.rail_unloaded", + "container.transport.transshipment_arrived", + "container.transport.transshipment_discharged", + "container.transport.transshipment_loaded", + "container.transport.transshipment_departed", + "container.transport.feeder_arrived", + "container.transport.feeder_discharged", + "container.transport.feeder_loaded", + "container.transport.feeder_departed", + "container.transport.empty_out", + "container.transport.full_in", + "container.transport.full_out", + "container.transport.empty_in", + "container.transport.vessel_berthed", + "shipment.estimated.arrival", + "tracking_request.succeeded", + "tracking_request.failed", + "tracking_request.awaiting_manifest", + "tracking_request.tracking_stopped", + "container.created", + "container.updated", + "container.pod_terminal_changed", + "container.transport.arrived_at_inland_destination", + "container.transport.estimated.arrived_at_inland_destination", + "container.pickup_lfd.changed" + ] + } + }, + "active": { + "type": "boolean" + }, + "headers": { + "type": "array", + "description": "Optional custom headers to pass with each webhook invocation", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of the header. (Please not this will be auto-capitalized) " + }, + "value": { + "type": "string", + "description": "The value to pass for the header\n" + } + } + } + } + } + }, + "type": { + "type": "string", + "enum": [ + "webhook" + ] + } + } + } + }, + "required": [ + "data" + ] + }, + "examples": {} + } + } + } + }, + "delete": { + "summary": "Delete a webhook", + "operationId": "delete-webhooks-id", + "responses": { + "200": { + "description": "OK" + } + }, + "description": "Delete a webhook", + "tags": [ + "Webhooks" + ] + } + }, + "/webhooks": { + "get": { + "summary": "List webhooks", + "tags": [ + "Webhooks" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/webhook" + } + }, + "meta": { + "$ref": "#/components/schemas/meta" + }, + "links": { + "$ref": "#/components/schemas/links" + } + } + }, + "examples": { + "example-1": { + "value": { + "data": [ + { + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "type": "webhook", + "attributes": { + "url": "http://example.com", + "active": true, + "events": [ + "tracking_request.succeeded" + ], + "secret": "672bd7b58b54645934a830d8fa", + "headers": [ + { + "name": "x-secret-sauce", + "value": "sriracha" + } + ] + } + } + ], + "meta": { + "size": 0, + "total": 0 + }, + "links": { + "last": "http://example.com", + "next": "http://example.com", + "prev": "http://example.com", + "first": "http://example.com", + "self": "http://example.com" + } + } + } + } + } + } + } + }, + "operationId": "get-webhooks", + "description": "Get a list of all the webhooks", + "parameters": [ + { + "schema": { + "type": "integer" + }, + "in": "query", + "name": "page[number]" + }, + { + "schema": { + "type": "integer" + }, + "in": "query", + "name": "page[size]" + } + ] + }, + "post": { + "summary": "Create a webhook", + "operationId": "post-webhooks", + "tags": [ + "Webhooks" + ], + "description": "You can configure a webhook via the API to be notified about events that happen in your Terminal49 account. These events can be realted to tracking_requests, shipments and containers. \n\nThis is the recommended way tracking shipments and containers via the API. You should use this instead of polling our the API periodically. ", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "type": "object", + "required": [ + "url", + "active" + ], + "properties": { + "url": { + "type": "string", + "example": "https://webhook.site/#!/39084fbb-d887-42e8-be08-b9183ad02362", + "format": "uri", + "description": "The URL of the webhook endpoint." + }, + "events": { + "type": "array", + "uniqueItems": true, + "description": "The list of events to enable for this endpoint.", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "container.transport.vessel_arrived", + "container.transport.vessel_discharged", + "container.transport.vessel_loaded", + "container.transport.vessel_departed", + "container.transport.rail_departed", + "container.transport.rail_arrived", + "container.transport.rail_loaded", + "container.transport.rail_unloaded", + "container.transport.transshipment_arrived", + "container.transport.transshipment_discharged", + "container.transport.transshipment_loaded", + "container.transport.transshipment_departed", + "container.transport.feeder_arrived", + "container.transport.feeder_discharged", + "container.transport.feeder_loaded", + "container.transport.feeder_departed", + "container.transport.empty_out", + "container.transport.full_in", + "container.transport.full_out", + "container.transport.empty_in", + "container.transport.vessel_berthed", + "shipment.estimated.arrival", + "tracking_request.succeeded", + "tracking_request.failed", + "tracking_request.awaiting_manifest", + "tracking_request.tracking_stopped", + "container.created", + "container.updated", + "container.pod_terminal_changed", + "container.transport.arrived_at_inland_destination", + "container.transport.estimated.arrived_at_inland_destination", + "container.pickup_lfd.changed" + ], + "example": "tracking_request.succeeded" + } + }, + "active": { + "type": "boolean" + }, + "headers": { + "type": "array", + "description": "Optional custom headers to pass with each webhook invocation", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of the header. (Please note this will be auto-capitalized) " + }, + "value": { + "type": "string", + "description": "The value to pass for the header\n" + } + } + } + } + } + }, + "type": { + "type": "string", + "enum": [ + "webhook" + ] + } + } + } + }, + "required": [ + "data" + ] + }, + "examples": { + "Test Webhook (all events)": { + "value": { + "data": { + "attributes": { + "url": "https://webhook.site/", + "events": [ + "container.transport.vessel_arrived", + "container.transport.vessel_discharged", + "container.transport.vessel_loaded", + "container.transport.vessel_departed", + "container.transport.rail_departed", + "container.transport.rail_arrived", + "container.transport.rail_loaded", + "container.transport.rail_unloaded", + "container.transport.transshipment_arrived", + "container.transport.transshipment_discharged", + "container.transport.transshipment_loaded", + "container.transport.transshipment_departed", + "container.transport.feeder_arrived", + "container.transport.feeder_discharged", + "container.transport.feeder_loaded", + "container.transport.feeder_departed", + "container.transport.empty_out", + "container.transport.full_in", + "container.transport.full_out", + "container.transport.empty_in", + "container.transport.vessel_berthed", + "shipment.estimated.arrival", + "tracking_request.succeeded", + "tracking_request.failed", + "tracking_request.awaiting_manifest", + "tracking_request.tracking_stopped", + "container.created", + "container.updated", + "container.pod_terminal_changed", + "container.transport.arrived_at_inland_destination", + "container.transport.estimated.arrived_at_inland_destination", + "container.pickup_lfd.changed" + ], + "active": true + }, + "type": "webhook" + } + } + } + } + } + }, + "description": "" + }, + "responses": { + "201": { + "description": "Create a test webhook endpoint", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/webhook" + } + } + }, + "examples": { + "Test Endpoint Created": { + "value": { + "data": { + "type": "webhook", + "id": "9809fb96-7754-488f-99df-29ca8d410d89", + "attributes": { + "url": "https://webhook.site/", + "active": true, + "events": [ + "tracking_request.succeeded" + ], + "secret": "C193J3QOXMFH", + "created_at": "2020-06-05T19:06:13Z" + } + } + } + } + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/webhook" + } + } + } + } + } + }, + "parameters": [] + }, + "/webhook_notifications/{id}": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "id", + "in": "path", + "required": true + } + ], + "get": { + "summary": "Get a single webhook notification", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/webhook_notification" + }, + "included": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/webhook" + }, + { + "$ref": "#/components/schemas/tracking_request" + }, + { + "$ref": "#/components/schemas/transport_event" + }, + { + "$ref": "#/components/schemas/estimated_event" + }, + { + "$ref": "#/components/schemas/container_updated_event" + } + ] + } + } + } + }, + "examples": { + "Tracking Request": { + "value": { + "data": { + "id": "a76187fc-5749-43f9-9053-cfaad9790a31", + "type": "webhook_notification", + "attributes": { + "id": "a76187fc-5749-43f9-9053-cfaad9790a31", + "event": "tracking_request.succeeded", + "delivery_status": "pending", + "created_at": "2020-09-11T21:25:34Z" + }, + "relationships": { + "reference_object": { + "data": { + "id": "bdeca506-9741-4ab1-a0a7-cfd1d908e923", + "type": "tracking_request" + } + }, + "webhook": { + "data": { + "id": "914b21ce-dd7d-4c49-8503-65aba488e9a9", + "type": "webhook" + } + }, + "webhook_notification_logs": { + "data": [] + } + } + }, + "included": [ + { + "id": "bdeca506-9741-4ab1-a0a7-cfd1d908e923", + "type": "tracking_request", + "attributes": { + "request_number": "TE497ED1063E", + "request_type": "bill_of_lading", + "scac": "MSCU", + "ref_numbers": [], + "created_at": "2020-09-11T21:25:34Z", + "updated_at": "2020-09-11T22:25:34Z", + "status": "created", + "failed_reason": null, + "is_retrying": false, + "retry_count": null + }, + "relationships": { + "tracked_object": { + "data": { + "id": "b5b10c0a-8d18-46da-b4c2-4e5fa790e7da", + "type": "shipment" + } + } + }, + "links": { + "self": "/v2/tracking_requests/bdeca506-9741-4ab1-a0a7-cfd1d908e923" + } + }, + { + "id": "b5b10c0a-8d18-46da-b4c2-4e5fa790e7da", + "type": "shipment", + "attributes": { + "created_at": "2020-09-11T21:25:33Z", + "bill_of_lading_number": "TE497ED1063E", + "ref_numbers": [], + "shipping_line_scac": "MSCU", + "shipping_line_name": "Mediterranean Shipping Company", + "port_of_lading_locode": "MXZLO", + "port_of_lading_name": "Manzanillo", + "port_of_discharge_locode": "USOAK", + "port_of_discharge_name": "Port of Oakland", + "pod_vessel_name": "MSC CHANNE", + "pod_vessel_imo": "9710438", + "pod_voyage_number": "098N", + "destination_locode": null, + "destination_name": null, + "destination_timezone": null, + "destination_ata_at": null, + "destination_eta_at": null, + "pol_etd_at": null, + "pol_atd_at": "2020-08-29T21:25:33Z", + "pol_timezone": "America/Mexico_City", + "pod_eta_at": "2020-09-18T21:25:33Z", + "pod_ata_at": null, + "pod_timezone": "America/Los_Angeles" + }, + "relationships": { + "port_of_lading": { + "data": { + "id": "4384d6a5-5ccc-43b7-8d19-4a9525e74c08", + "type": "port" + } + }, + "port_of_discharge": { + "data": { + "id": "2a765fdd-c479-4345-b71d-c4ef839952e2", + "type": "port" + } + }, + "pod_terminal": { + "data": { + "id": "17891bc8-52da-40bf-8ff0-0247ec05faf1", + "type": "terminal" + } + }, + "destination": { + "data": null + }, + "containers": { + "data": [ + { + "id": "b2fc728c-e2f5-4a99-8899-eb7b34ef22d7", + "type": "container" + } + ] + } + }, + "links": { + "self": "/v2/shipments/b5b10c0a-8d18-46da-b4c2-4e5fa790e7da" + } + }, + { + "id": "b2fc728c-e2f5-4a99-8899-eb7b34ef22d7", + "type": "container", + "attributes": { + "number": "ARDU1824900", + "seal_number": "139F1451", + "created_at": "2020-09-11T21:25:34Z", + "equipment_type": "dry", + "equipment_length": 40, + "equipment_height": "standard", + "weight_in_lbs": 53507, + "fees_at_pod_terminal": [], + "holds_at_pod_terminal": [], + "pickup_lfd": null, + "pickup_appointment_at": null, + "availability_known": true, + "available_for_pickup": false, + "pod_arrived_at": null, + "pod_discharged_at": null, + "final_destination_full_out_at": null, + "pod_full_out_at": null, + "empty_terminated_at": null, + "pod_timezone": "America/Los_Angeles", + "final_destination_timezone": null, + "empty_terminated_timezone": null + }, + "relationships": { + "shipment": { + "data": { + "id": "b5b10c0a-8d18-46da-b4c2-4e5fa790e7da", + "type": "shipment" + } + }, + "pod_terminal": { + "data": { + "id": "17891bc8-52da-40bf-8ff0-0247ec05faf1", + "type": "terminal" + } + }, + "transport_events": { + "data": [ + { + "id": "56078596-5293-4c84-9245-cca00a787265", + "type": "transport_event" + } + ] + } + } + }, + { + "id": "56078596-5293-4c84-9245-cca00a787265", + "type": "transport_event", + "attributes": { + "event": "container.transport.vessel_departed", + "created_at": "2020-09-11T21:25:34Z", + "voyage_number": null, + "timestamp": "2020-08-29T21:25:33Z", + "location_locode": "MXZLO", + "timezone": "America/Los_Angeles" + }, + "relationships": { + "shipment": { + "data": { + "id": "b5b10c0a-8d18-46da-b4c2-4e5fa790e7da", + "type": "shipment" + } + }, + "container": { + "data": { + "id": "b2fc728c-e2f5-4a99-8899-eb7b34ef22d7", + "type": "container" + } + }, + "vessel": { + "data": null + }, + "location": { + "data": { + "id": "2a765fdd-c479-4345-b71d-c4ef839952e2", + "type": "port" + } + }, + "terminal": { + "data": null + } + } + } + ] + } + }, + "Estimated Event": { + "value": { + "data": { + "id": "d7e04138-b59d-4c41-9d2d-251d95bedd6e", + "type": "webhook_notification", + "attributes": { + "id": "d7e04138-b59d-4c41-9d2d-251d95bedd6e", + "event": "shipment.estimated.arrival", + "delivery_status": "pending", + "created_at": "2020-09-11T21:25:34Z" + }, + "relationships": { + "reference_object": { + "data": { + "id": "b68bc6cb-2c37-43f6-889b-86a16b2b6fe6", + "type": "estimated_event" + } + }, + "webhook": { + "data": { + "id": "614eab61-ae3c-4d40-bbe9-41200a172691", + "type": "webhook" + } + } + } + }, + "included": [ + { + "id": "b68bc6cb-2c37-43f6-889b-86a16b2b6fe6", + "type": "estimated_event", + "attributes": { + "created_at": "2020-04-06T19:02:46-07:00", + "estimated_timestamp": "2020-04-09T19:02:46-07:00", + "voyage_number": "A1C", + "event": "shipment.estimated.arrival", + "timezone": "America/Los_Angeles" + }, + "relationships": { + "shipment": { + "data": { + "id": "715ed64b-6195-49f6-9407-1383a8088bfd", + "type": "shipment" + } + }, + "port": { + "data": { + "id": "ed4001a5-ad9d-43c3-883c-79354f422510", + "type": "port" + } + }, + "vessel": { + "data": { + "id": "ebf68c6c-9d0d-4383-aa41-e097009dfb4c", + "type": "vessel" + } + } + } + }, + { + "id": "ed4001a5-ad9d-43c3-883c-79354f422510", + "type": "port", + "attributes": { + "id": "ed4001a5-ad9d-43c3-883c-79354f422510", + "name": "Port of Oakland", + "code": "USOAK", + "state_abbr": "CA", + "city": "Oakland", + "country_code": "US", + "time_zone": "America/Los_Angeles" + } + }, + { + "id": "715ed64b-6195-49f6-9407-1383a8088bfd", + "type": "shipment", + "attributes": { + "created_at": "2020-04-06T19:02:46-07:00", + "bill_of_lading_number": "TE49DD6650B9", + "ref_numbers": [ + "REF-4A25EA" + ], + "shipping_line_scac": "MSCU", + "shipping_line_name": "Mediterranean Shipping Company", + "port_of_lading_locode": "MXZLO", + "port_of_lading_name": "Manzanillo", + "port_of_discharge_locode": "USOAK", + "port_of_discharge_name": "Port of Oakland", + "pod_vessel_name": "MSC CHANNE", + "pod_vessel_imo": "9710438", + "pod_voyage_number": "098N", + "destination_locode": null, + "destination_name": null, + "destination_timezone": null, + "destination_ata_at": null, + "destination_eta_at": null, + "pol_etd_at": null, + "pol_atd_at": null, + "pol_timezone": "America/Mexico_City", + "pod_eta_at": "2020-04-13T19:02:46-07:00", + "pod_ata_at": null, + "pod_timezone": "America/Los_Angeles" + }, + "relationships": { + "port_of_lading": { + "data": { + "id": "1378c720-efe9-4562-a2ad-562002eb4b1d", + "type": "port" + } + }, + "port_of_discharge": { + "data": { + "id": "ed4001a5-ad9d-43c3-883c-79354f422510", + "type": "port" + } + }, + "pod_terminal": { + "data": { + "id": "2508d879-4451-4d7f-ab23-92258b5df553", + "type": "terminal" + } + }, + "destination": { + "data": null + }, + "containers": { + "data": [] + } + }, + "links": { + "self": "/v2/shipments/715ed64b-6195-49f6-9407-1383a8088bfd" + } + } + ] + } + }, + "Transport Event": { + "value": { + "data": { + "id": "abec839a-48fe-4540-93d7-d3ea3d67bdbf", + "type": "webhook_notification", + "attributes": { + "id": "abec839a-48fe-4540-93d7-d3ea3d67bdbf", + "event": "container.transport.vessel_arrived", + "delivery_status": "pending", + "created_at": "2020-07-28T23:12:53Z" + }, + "relationships": { + "reference_object": { + "data": { + "id": "a6ecb8ab-98d6-4cab-8487-ce9dd7be082b", + "type": "transport_event" + } + }, + "webhook": { + "data": { + "id": "534d498b-8332-439a-accb-129dfd144ceb", + "type": "webhook" + } + }, + "webhook_notification_logs": { + "data": [] + } + } + }, + "included": [ + { + "id": "a6ecb8ab-98d6-4cab-8487-ce9dd7be082b", + "type": "transport_event", + "attributes": { + "event": "container.transport.vessel_arrived", + "created_at": "2020-07-28T23:12:53Z", + "voyage_number": null, + "timestamp": "2020-07-28T23:12:53Z", + "timezone": "America/Los_Angeles" + }, + "relationships": { + "shipment": { + "data": { + "id": "1fc35241-4c8b-420d-803a-9e6661720a05", + "type": "shipment" + } + }, + "container": { + "data": { + "id": "8c2f335a-b155-4021-87f0-9b040159a981", + "type": "container" + } + }, + "vessel": { + "data": { + "id": "b381c692-8dad-4f04-873f-d9e567143335", + "type": "vessel" + } + }, + "location": { + "data": { + "id": "f5a8a49f-d8b2-4d2a-8a43-0e4ff0ce7995", + "type": "port" + } + }, + "terminal": { + "data": { + "id": "26fede8d-2c6d-4bf5-98d6-5a86d30f17a9", + "type": "terminal" + } + } + } + }, + { + "id": "f5a8a49f-d8b2-4d2a-8a43-0e4ff0ce7995", + "type": "port", + "attributes": { + "id": "f5a8a49f-d8b2-4d2a-8a43-0e4ff0ce7995", + "name": "Port of Oakland", + "code": "USOAK", + "state_abbr": "CA", + "city": "Oakland", + "country_code": "US", + "time_zone": "America/Los_Angeles" + } + }, + { + "id": "1fc35241-4c8b-420d-803a-9e6661720a05", + "type": "shipment", + "attributes": { + "created_at": "2020-07-28T23:12:53Z", + "bill_of_lading_number": "TE491846459E", + "ref_numbers": [ + null + ], + "shipping_line_scac": "MSCU", + "shipping_line_name": "Mediterranean Shipping Company", + "port_of_lading_locode": "MXZLO", + "port_of_lading_name": "Manzanillo", + "port_of_discharge_locode": "USOAK", + "port_of_discharge_name": "Port of Oakland", + "pod_vessel_name": "MSC CHANNE", + "pod_vessel_imo": "9710438", + "pod_voyage_number": "098N", + "destination_locode": null, + "destination_name": null, + "destination_timezone": null, + "destination_ata_at": null, + "destination_eta_at": null, + "pol_etd_at": null, + "pol_atd_at": "2020-07-15T23:12:53Z", + "pol_timezone": "America/Mexico_City", + "pod_eta_at": "2020-08-04T23:12:53Z", + "pod_ata_at": null, + "pod_timezone": "America/Los_Angeles" + }, + "relationships": { + "port_of_lading": { + "data": { + "id": "06564cb7-77d6-4e0e-8e4a-37756ca21bc9", + "type": "port" + } + }, + "port_of_discharge": { + "data": { + "id": "f5a8a49f-d8b2-4d2a-8a43-0e4ff0ce7995", + "type": "port" + } + }, + "pod_terminal": { + "data": { + "id": "06f5d3bb-f258-4f1b-8c2f-db78248f6e29", + "type": "terminal" + } + }, + "destination": { + "data": null + }, + "containers": { + "data": [ + { + "id": "8c2f335a-b155-4021-87f0-9b040159a981", + "type": "container" + } + ] + } + }, + "links": { + "self": "/v2/shipments/1fc35241-4c8b-420d-803a-9e6661720a05" + } + } + ] + } + }, + "Container Updated Event": { + "value": { + "data": { + "id": "416e293f-4423-47f7-abf3-1ae97054f41f", + "type": "webhook_notification", + "attributes": { + "id": "416e293f-4423-47f7-abf3-1ae97054f41f", + "event": "container.updated", + "delivery_status": "pending", + "created_at": "2020-06-04T22:03:09Z" + }, + "relationships": { + "reference_object": { + "data": { + "id": "fc48cb10-b7a8-47a4-a12f-89bce7434978", + "type": "container_updated_event" + } + }, + "webhook": { + "data": { + "id": "cda37836-aa40-455e-8b43-5fd74930c7f6", + "type": "webhook" + } + }, + "webhook_notification_logs": { + "data": [] + } + } + }, + "included": [ + { + "id": "fc48cb10-b7a8-47a4-a12f-89bce7434978", + "type": "container_updated_event", + "attributes": { + "changeset": { + "available_for_pickup": [ + false, + true + ], + "pod_terminal_holds": [ + null, + [ + { + "name": "customs", + "status": "hold", + "description": "CUST EXAM" + } + ] + ] + }, + "timestamp": "2020-06-04T22:03:09Z", + "timezone": "America/Los_Angeles" + }, + "relationships": { + "container": { + "data": { + "id": "1445af31-991c-4d52-a183-6c3ea97cd6e8", + "type": "container" + } + }, + "terminal": { + "data": { + "id": "07db0258-1911-4acf-8e70-0cfe4b100f80", + "type": "terminal" + } + } + } + }, + { + "id": "1445af31-991c-4d52-a183-6c3ea97cd6e8", + "type": "container", + "attributes": { + "number": "GLDU1355602", + "seal_number": "431ac97412228532", + "created_at": "2020-05-04T22:03:09Z", + "equipment_type": "dry", + "equipment_length": 40, + "equipment_height": "standard", + "weight_in_lbs": 55634, + "fees_at_pod_terminal": [], + "holds_at_pod_terminal": [], + "pickup_lfd": null, + "availability_known": true, + "available_for_pickup": null, + "pod_arrived_at": "2020-06-04T22:03:08Z", + "pod_discharged_at": "2020-06-04T22:03:08Z", + "final_destination_full_out_at": "2020-06-04T22:03:08Z", + "pod_full_out_at": null, + "empty_terminated_at": null, + "pod_timezone": "America/Los_Angeles", + "final_destination_timezone": null, + "empty_terminated_timezone": null + }, + "relationships": { + "shipment": { + "data": null + } + } + }, + { + "id": "07db0258-1911-4acf-8e70-0cfe4b100f80", + "type": "terminal", + "attributes": { + "id": "07db0258-1911-4acf-8e70-0cfe4b100f80", + "nickname": "Denesik-Hintz", + "name": "Adams LLC Terminal", + "firms_code": "E005" + }, + "relationships": { + "port": { + "data": { + "id": "d8a92775-95f9-47be-a6d2-42542a32d5fc", + "type": "port" + } + } + } + } + ] + } + } + } + } + } + } + }, + "operationId": "get-webhook-notification-id", + "description": "\n", + "tags": [ + "Webhook Notifications" + ], + "parameters": [ + { + "schema": { + "type": "string" + }, + "in": "query", + "description": "Comma delimited list of relations to include.", + "name": "include" + } + ] + } + }, + "/webhook_notifications": { + "get": { + "summary": "List webhook notifications", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/webhook_notification" + } + }, + "links": { + "$ref": "#/components/schemas/links" + }, + "meta": { + "$ref": "#/components/schemas/meta" + }, + "included": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/webhook" + }, + { + "$ref": "#/components/schemas/tracking_request" + }, + { + "$ref": "#/components/schemas/transport_event" + }, + { + "$ref": "#/components/schemas/estimated_event" + } + ] + } + } + } + } + } + } + } + }, + "operationId": "get-webhook-notifications", + "description": "Return the list of webhook notifications. This can be useful for reconciling your data if your endpoint has been down. ", + "tags": [ + "Webhook Notifications" + ], + "parameters": [ + { + "schema": { + "type": "integer" + }, + "in": "query", + "name": "page[number]" + }, + { + "schema": { + "type": "integer" + }, + "in": "query", + "name": "page[size]" + }, + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "include", + "description": "Comma delimited list of relations to include." + } + ] + } + }, + "/webhook_notifications/examples": { + "get": { + "summary": "Get webhook notification payload examples", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/webhook_notification" + } + }, + "links": { + "$ref": "#/components/schemas/links" + }, + "meta": { + "$ref": "#/components/schemas/meta" + }, + "included": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/webhook" + }, + { + "$ref": "#/components/schemas/tracking_request" + }, + { + "$ref": "#/components/schemas/transport_event" + }, + { + "$ref": "#/components/schemas/estimated_event" + } + ] + } + } + } + } + } + } + } + }, + "operationId": "get-webhook-notifications-example", + "description": "Returns an example payload as it would be sent to a webhook endpoint for the provided `event` ", + "tags": [ + "Webhook Notifications" + ], + "parameters": [ + { + "schema": { + "type": "string", + "enum": [ + "container.transport.vessel_arrived", + "container.transport.vessel_discharged", + "container.transport.vessel_loaded", + "container.transport.vessel_departed", + "container.transport.rail_departed", + "container.transport.rail_arrived", + "container.transport.rail_loaded", + "container.transport.rail_unloaded", + "container.transport.transshipment_arrived", + "container.transport.transshipment_discharged", + "container.transport.transshipment_loaded", + "container.transport.transshipment_departed", + "container.transport.feeder_arrived", + "container.transport.feeder_discharged", + "container.transport.feeder_loaded", + "container.transport.feeder_departed", + "container.transport.empty_out", + "container.transport.full_in", + "container.transport.full_out", + "container.transport.empty_in", + "container.transport.vessel_berthed", + "shipment.estimated.arrival", + "tracking_request.succeeded", + "tracking_request.failed", + "tracking_request.awaiting_manifest", + "tracking_request.tracking_stopped", + "container.created", + "container.updated", + "container.pod_terminal_changed", + "container.transport.arrived_at_inland_destination", + "container.transport.estimated.arrived_at_inland_destination", + "container.pickup_lfd.changed" + ], + "example": "container.transport.full_out" + }, + "in": "query", + "name": "event", + "description": "The webhook notification event name you wish to see an example of" + } + ] + }, + "parameters": [] + }, + "/webhooks/ips": { + "get": { + "summary": "List webhook IPs", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "webhook_notification_ips": { + "type": "array", + "items": { + "type": "string", + "format": "ipv4" + } + }, + "last_updated": { + "type": "string", + "format": "date-time" + } + } + }, + "examples": { + "Example List of IPs": { + "value": { + "webhook_notification_ips": [ + "35.222.62.171", + "3.230.67.145", + "44.217.15.129" + ], + "last_updated": "2023-10-17T21:23:16Z" + } + } + } + } + } + } + }, + "operationId": "get-webhooks-ips", + "description": "Return the list of IPs used for sending webhook notifications. This can be useful for whitelisting the IPs on the firewall.", + "tags": [ + "Webhooks" + ], + "parameters": [] + } + }, + "/containers": { + "get": { + "summary": "List containers", + "tags": [ + "Containers" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/container" + } + }, + "included": { + "type": "array", + "items": { + "$ref": "#/components/schemas/shipment" + } + }, + "links": { + "$ref": "#/components/schemas/links" + }, + "meta": { + "$ref": "#/components/schemas/meta" + } + } + }, + "examples": { + "Example List of containers": { + "value": { + "data": [ + { + "id": "be0b247b-c144-4163-8919-cf9178930736", + "type": "container", + "attributes": { + "number": "TCLU6718159", + "seal_number": null, + "created_at": "2024-06-26T15:05:18Z", + "ref_numbers": [], + "pod_arrived_at": "2024-06-21T14:12:00Z", + "pod_discharged_at": "2024-06-23T00:19:00Z", + "final_destination_full_out_at": null, + "holds_at_pod_terminal": [], + "available_for_pickup": false, + "equipment_type": "dry", + "equipment_length": 40, + "equipment_height": "high_cube", + "weight_in_lbs": 53502, + "pod_full_out_at": "2024-06-26T16:15:00Z", + "empty_terminated_at": null, + "terminal_checked_at": "2024-06-26T18:47:56Z", + "fees_at_pod_terminal": [], + "pickup_lfd": null, + "pickup_appointment_at": null, + "pod_full_out_chassis_number": null, + "location_at_pod_terminal": "Community", + "pod_last_tracking_request_at": "2024-06-26T18:47:56Z", + "shipment_last_tracking_request_at": "2024-06-26T15:05:18Z", + "availability_known": true, + "pod_timezone": "America/New_York", + "final_destination_timezone": null, + "empty_terminated_timezone": "America/New_York", + "pod_rail_carrier_scac": null, + "ind_rail_carrier_scac": null, + "pod_rail_loaded_at": null, + "pod_rail_departed_at": null, + "ind_eta_at": null, + "ind_ata_at": null, + "ind_rail_unloaded_at": null, + "ind_facility_lfd_on": null + }, + "relationships": { + "shipment": { + "data": { + "id": "cc8f8e43-d6a9-4edb-a8c0-d0ab03c113d3", + "type": "shipment" + } + }, + "pickup_facility": { + "data": null + }, + "pod_terminal": { + "data": { + "id": "a243bdf8-0da3-4056-a6a7-05fe8ab43999", + "type": "terminal" + } + }, + "transport_events": { + "data": [ + { + "id": "fb53b35c-6a0c-4e22-b196-b623e8ba7db5", + "type": "transport_event" + }, + { + "id": "179897a3-04f5-450b-86e1-db57970c0248", + "type": "transport_event" + }, + { + "id": "a119b8e6-10c5-4967-8364-885f7dbf8e50", + "type": "transport_event" + }, + { + "id": "3372d58c-df89-46a2-b064-b308a9dc7040", + "type": "transport_event" + }, + { + "id": "800a3e59-8231-4e42-a80f-73c97cfb1be9", + "type": "transport_event" + }, + { + "id": "d466676b-0073-4b0c-89aa-d42486e9ed4f", + "type": "transport_event" + }, + { + "id": "85665836-5915-4cb9-ab78-b8487598cd0d", + "type": "transport_event" + }, + { + "id": "94cca22a-520a-4d19-a551-0554aceb3794", + "type": "transport_event" + }, + { + "id": "032e812f-d5e4-48af-8d79-2c2b41a07032", + "type": "transport_event" + }, + { + "id": "1b7dd74d-eff6-4e06-9a15-234295ce6fd5", + "type": "transport_event" + }, + { + "id": "b9f07b7e-1653-4209-b375-4588653d5275", + "type": "transport_event" + }, + { + "id": "a35f3c1b-ad35-4347-b0f2-9e08f0d4ca64", + "type": "transport_event" + } + ] + }, + "raw_events": { + "data": [ + { + "id": "e26fc659-f79d-4cc0-8efc-5ce5e8444891", + "type": "raw_event" + }, + { + "id": "8820af5e-cb77-41c5-a897-906f2c56eb1e", + "type": "raw_event" + }, + { + "id": "a628a2c2-dab6-4b04-b3ae-d7ec99098a89", + "type": "raw_event" + }, + { + "id": "6ca157f9-3b58-4db5-8155-fc7b41a62611", + "type": "raw_event" + }, + { + "id": "6fb06390-4b0a-4c1f-9703-ec89927df7f3", + "type": "raw_event" + }, + { + "id": "26fe408d-e091-412a-a2fb-23a4d778b6b9", + "type": "raw_event" + }, + { + "id": "16490dd8-d79f-468a-91b1-dbb30bb45c85", + "type": "raw_event" + }, + { + "id": "ff3db923-a644-4706-8057-1bd53c95fbd5", + "type": "raw_event" + }, + { + "id": "3fd27ffd-9618-477f-b1a9-cbc179defefe", + "type": "raw_event" + }, + { + "id": "f92efdd3-f79c-4a1c-97ce-9a47588b525c", + "type": "raw_event" + }, + { + "id": "3b2a88ef-df0b-4e61-99c1-d4a175910111", + "type": "raw_event" + }, + { + "id": "9b367e33-9e43-488f-8217-081698adf40d", + "type": "raw_event" + }, + { + "id": "ee0915df-e2f8-46b0-acf1-816871ca142d", + "type": "raw_event" + } + ] + } + } + }, + { + "id": "a9e52f3d-2fa9-467c-8deb-09e90dac2f0b", + "type": "container", + "attributes": { + "number": "TCLU2224327", + "seal_number": null, + "created_at": "2024-06-26T15:05:34Z", + "ref_numbers": [], + "pod_arrived_at": "2024-06-21T14:12:00Z", + "pod_discharged_at": "2024-06-23T17:21:00Z", + "final_destination_full_out_at": null, + "holds_at_pod_terminal": [], + "available_for_pickup": false, + "equipment_type": "dry", + "equipment_length": 20, + "equipment_height": "standard", + "weight_in_lbs": 44225, + "pod_full_out_at": null, + "empty_terminated_at": null, + "terminal_checked_at": "2024-06-26T18:47:56Z", + "fees_at_pod_terminal": [], + "pickup_lfd": null, + "pickup_appointment_at": null, + "pod_full_out_chassis_number": null, + "location_at_pod_terminal": "Yard", + "pod_last_tracking_request_at": "2024-06-26T18:47:56Z", + "shipment_last_tracking_request_at": "2024-06-26T15:05:34Z", + "availability_known": true, + "pod_timezone": "America/New_York", + "final_destination_timezone": "America/Chicago", + "empty_terminated_timezone": "America/Chicago", + "pod_rail_carrier_scac": "CSXT", + "ind_rail_carrier_scac": "CSXT", + "pod_rail_loaded_at": null, + "pod_rail_departed_at": null, + "ind_eta_at": "2024-07-02T14:20:00Z", + "ind_ata_at": null, + "ind_rail_unloaded_at": null, + "ind_facility_lfd_on": null + }, + "relationships": { + "shipment": { + "data": { + "id": "99f84294-3bda-4765-81b3-31765e6d2a24", + "type": "shipment" + } + }, + "pickup_facility": { + "data": { + "id": "e6fa9a01-511b-4f43-a7e1-d628315b84ef", + "type": "terminal" + } + }, + "pod_terminal": { + "data": { + "id": "a243bdf8-0da3-4056-a6a7-05fe8ab43999", + "type": "terminal" + } + }, + "transport_events": { + "data": [ + { + "id": "4a5a04b7-8974-4c46-beaa-bf55004422c9", + "type": "transport_event" + }, + { + "id": "11c90391-aa9c-408b-b20e-daed8dd09586", + "type": "transport_event" + }, + { + "id": "d7f23c71-7084-4fbb-9fef-740f624182aa", + "type": "transport_event" + }, + { + "id": "c4c3537d-5c47-4623-afe0-edc0dd6f75c5", + "type": "transport_event" + }, + { + "id": "e4002e13-0a74-4bd4-9147-787bb21e2fda", + "type": "transport_event" + }, + { + "id": "57b5568f-d8a6-443d-b1c8-be5cd080e5ed", + "type": "transport_event" + }, + { + "id": "0771cff1-79bf-4eaa-9d9d-790cb433ce44", + "type": "transport_event" + }, + { + "id": "031021b9-7b39-41f7-bd45-26cc8cb799d2", + "type": "transport_event" + }, + { + "id": "9956448d-34d3-4c23-bcfd-19807eb4034f", + "type": "transport_event" + }, + { + "id": "f1caf6ea-3e92-4b68-bcea-556828301062", + "type": "transport_event" + }, + { + "id": "2dd558a8-fa07-454c-acf3-b53d072264af", + "type": "transport_event" + }, + { + "id": "83118511-d9ed-4b16-b323-5e685d9b266e", + "type": "transport_event" + } + ] + }, + "raw_events": { + "data": [ + { + "id": "f256289f-219d-45f6-b727-56fb9c6bc433", + "type": "raw_event" + }, + { + "id": "c5581f60-ffb2-4ef0-a855-b70acd3b294f", + "type": "raw_event" + }, + { + "id": "3c6716f9-814c-4459-9bbb-753f010446b2", + "type": "raw_event" + }, + { + "id": "62c7b849-c99b-4e37-9861-105141cc0a4c", + "type": "raw_event" + }, + { + "id": "7db37d43-426a-4f84-82af-2957621ce466", + "type": "raw_event" + }, + { + "id": "d4342119-db56-46fc-8299-f573f5b52e73", + "type": "raw_event" + }, + { + "id": "ba159c3b-590c-43ff-bc04-b0354fd326f4", + "type": "raw_event" + }, + { + "id": "ec1be31a-17f1-4f6f-85ab-c74cb0dbe6cb", + "type": "raw_event" + }, + { + "id": "d12c4656-0d1f-4f30-a0fa-a2ee887741a8", + "type": "raw_event" + }, + { + "id": "146d56e2-b41d-4469-8202-0c7d7315e794", + "type": "raw_event" + }, + { + "id": "6b86b4d1-f85f-4c26-9fbc-3132a62f0fbc", + "type": "raw_event" + }, + { + "id": "bb2c9105-e421-4372-9265-e61b3fa54851", + "type": "raw_event" + }, + { + "id": "3a09c49f-c12e-49e9-b2de-b8dca2b3d608", + "type": "raw_event" + }, + { + "id": "54d91ebc-8f57-4764-b7c6-a3f4dc2459be", + "type": "raw_event" + }, + { + "id": "9e788c46-9431-41eb-baee-c8b14fb4f590", + "type": "raw_event" + }, + { + "id": "d3051802-8b6f-497d-ad16-fb35547c8662", + "type": "raw_event" + }, + { + "id": "af3090e7-c6d3-4d8e-88aa-9cd757102f9b", + "type": "raw_event" + }, + { + "id": "95daf204-d95d-4a89-bedf-358bedb8b3b8", + "type": "raw_event" + } + ] + } + } + }, + { + "id": "8d1faeeb-3890-4fac-8659-cd13737b26f1", + "type": "container", + "attributes": { + "number": "CMAU0619052", + "seal_number": null, + "created_at": "2024-06-26T15:02:11Z", + "ref_numbers": [], + "pod_arrived_at": "2024-06-22T22:10:00Z", + "pod_discharged_at": "2024-06-23T20:38:00Z", + "final_destination_full_out_at": null, + "holds_at_pod_terminal": [], + "available_for_pickup": false, + "equipment_type": "dry", + "equipment_length": 20, + "equipment_height": "standard", + "weight_in_lbs": null, + "pod_full_out_at": null, + "empty_terminated_at": null, + "terminal_checked_at": "2024-06-26T18:47:47Z", + "fees_at_pod_terminal": [], + "pickup_lfd": "2024-06-27T07:00:00Z", + "pickup_appointment_at": null, + "pod_full_out_chassis_number": null, + "location_at_pod_terminal": "Grounded", + "pod_last_tracking_request_at": "2024-06-26T18:47:47Z", + "shipment_last_tracking_request_at": "2024-06-26T15:02:11Z", + "availability_known": true, + "pod_timezone": "America/Los_Angeles", + "final_destination_timezone": "Asia/Shanghai", + "empty_terminated_timezone": "Asia/Shanghai", + "pod_rail_carrier_scac": null, + "ind_rail_carrier_scac": null, + "pod_rail_loaded_at": null, + "pod_rail_departed_at": null, + "ind_eta_at": null, + "ind_ata_at": null, + "ind_rail_unloaded_at": null, + "ind_facility_lfd_on": null + }, + "relationships": { + "shipment": { + "data": { + "id": "f706cbea-3264-473d-8d26-af257f3bc1be", + "type": "shipment" + } + }, + "pickup_facility": { + "data": null + }, + "pod_terminal": { + "data": { + "id": "eaa2580c-5f5b-4198-85e4-821145d62098", + "type": "terminal" + } + }, + "transport_events": { + "data": [ + { + "id": "1bb3a814-edab-403f-8ef2-a6d0966df423", + "type": "transport_event" + }, + { + "id": "19f197bf-444c-40ee-8478-6f02abe715a9", + "type": "transport_event" + }, + { + "id": "4c9223bb-0218-4175-8a2e-7bb99c40642a", + "type": "transport_event" + }, + { + "id": "432da964-6e99-45e9-b4b1-00b7be858591", + "type": "transport_event" + }, + { + "id": "b462b4d1-1e02-4037-af7f-6c8fa981f268", + "type": "transport_event" + }, + { + "id": "e3ca4a25-692f-474a-aa71-48fb9840aef1", + "type": "transport_event" + }, + { + "id": "014b9d1f-f033-4a3e-89f9-c57569883436", + "type": "transport_event" + }, + { + "id": "2cec71c9-721a-4060-993f-0ffcf01151cd", + "type": "transport_event" + }, + { + "id": "24941515-1b5e-4fca-87eb-092e56ed156a", + "type": "transport_event" + }, + { + "id": "0fd06bc0-1fda-467c-ab67-90a30c6d62ab", + "type": "transport_event" + }, + { + "id": "0bce915e-b9ba-42a0-a484-136266fe8b9a", + "type": "transport_event" + }, + { + "id": "582006aa-6547-4712-b964-5637aad839b4", + "type": "transport_event" + }, + { + "id": "11458420-b354-4288-a275-7572d3c60e33", + "type": "transport_event" + }, + { + "id": "5c900fa1-11bf-4f96-89e7-b12f6a98edc4", + "type": "transport_event" + } + ] + }, + "raw_events": { + "data": [ + { + "id": "999d7de7-eaed-4313-85df-772a6d24a85e", + "type": "raw_event" + }, + { + "id": "ac9c2780-240d-443f-87f4-95465fa5447b", + "type": "raw_event" + }, + { + "id": "a0ee3724-91c3-4b78-af32-2f16e8c2d600", + "type": "raw_event" + }, + { + "id": "79725b1f-19f7-4fc3-8c69-586e237c1719", + "type": "raw_event" + }, + { + "id": "4f07f257-ea4f-4e61-94ed-a01598899020", + "type": "raw_event" + }, + { + "id": "41612acb-b3d4-495f-9138-f34105851d21", + "type": "raw_event" + }, + { + "id": "84ffdd37-ec39-404a-9b68-d72d8fb96d48", + "type": "raw_event" + }, + { + "id": "7e9d8a6d-5339-4266-a6fb-22c28f41149f", + "type": "raw_event" + }, + { + "id": "2359b787-b218-42dc-b9a5-84b608aee671", + "type": "raw_event" + }, + { + "id": "ea33303e-0e48-4442-9886-0dfe38b726b5", + "type": "raw_event" + }, + { + "id": "3689d013-8525-418b-92ed-95ec684130b4", + "type": "raw_event" + }, + { + "id": "a64f7f7e-a6fa-4913-aba0-b28ba189d68b", + "type": "raw_event" + }, + { + "id": "c264a859-4fcb-4fab-95c0-29be99ec54a4", + "type": "raw_event" + }, + { + "id": "2aabf25c-9a3e-44bc-b6c8-ed7b1e3c3630", + "type": "raw_event" + }, + { + "id": "6ae70038-02d7-425a-99c1-8761c69a9033", + "type": "raw_event" + }, + { + "id": "c30021d8-93e9-4bfd-a978-e9e24de148c8", + "type": "raw_event" + } + ] + } + } + }, + { + "id": "853f1794-9b94-4118-9970-4e28e549440d", + "type": "container", + "attributes": { + "number": "TGHU6578122", + "seal_number": null, + "created_at": "2024-06-26T15:08:30Z", + "ref_numbers": [], + "pod_arrived_at": "2024-06-23T21:58:00Z", + "pod_discharged_at": "2024-06-24T02:28:00Z", + "final_destination_full_out_at": null, + "holds_at_pod_terminal": [], + "available_for_pickup": false, + "equipment_type": "dry", + "equipment_length": 40, + "equipment_height": "high_cube", + "weight_in_lbs": 8898, + "pod_full_out_at": null, + "empty_terminated_at": null, + "terminal_checked_at": "2024-06-26T18:47:47Z", + "fees_at_pod_terminal": [], + "pickup_lfd": "2024-06-27T07:00:00Z", + "pickup_appointment_at": null, + "pod_full_out_chassis_number": null, + "location_at_pod_terminal": "Wheeled", + "pod_last_tracking_request_at": "2024-06-26T18:47:46Z", + "shipment_last_tracking_request_at": "2024-06-26T15:08:30Z", + "availability_known": true, + "pod_timezone": "America/Los_Angeles", + "final_destination_timezone": "America/New_York", + "empty_terminated_timezone": "America/New_York", + "pod_rail_carrier_scac": "BNSF", + "ind_rail_carrier_scac": "CSXT", + "pod_rail_loaded_at": null, + "pod_rail_departed_at": null, + "ind_eta_at": "2024-07-07T08:00:00Z", + "ind_ata_at": null, + "ind_rail_unloaded_at": null, + "ind_facility_lfd_on": null + }, + "relationships": { + "shipment": { + "data": { + "id": "f3cfe624-706e-4a0c-89d5-140980d986fd", + "type": "shipment" + } + }, + "pickup_facility": { + "data": { + "id": "7e4557b9-cc5a-4298-aaec-1a32e90202c9", + "type": "terminal" + } + }, + "pod_terminal": { + "data": { + "id": "eaa2580c-5f5b-4198-85e4-821145d62098", + "type": "terminal" + } + }, + "transport_events": { + "data": [ + { + "id": "94f687d0-dc6b-4342-8710-cb98bc98716e", + "type": "transport_event" + }, + { + "id": "a8d0a842-95fe-4c12-a80e-bd12e87a1421", + "type": "transport_event" + }, + { + "id": "c1f1a186-3737-41ca-ae2b-f79a17519991", + "type": "transport_event" + }, + { + "id": "7fcc5496-d402-4b1c-a6c5-8514e6433070", + "type": "transport_event" + }, + { + "id": "ab7cd84a-edc5-476d-a1c4-190011582314", + "type": "transport_event" + }, + { + "id": "68d91384-3eb3-4d21-ac7a-c1f688f649c2", + "type": "transport_event" + }, + { + "id": "5a702f46-4356-4570-bd2e-2b8adab5ba3e", + "type": "transport_event" + } + ] + }, + "raw_events": { + "data": [ + { + "id": "a4b7e4b6-5227-4f34-9e91-9e75223798ae", + "type": "raw_event" + }, + { + "id": "4f3e3c7b-d1e6-4fde-b95e-ce9a8835445c", + "type": "raw_event" + }, + { + "id": "39e8570d-1a8f-4e10-8d2e-ac930abc5971", + "type": "raw_event" + }, + { + "id": "b8015a72-9741-4fbf-8adc-d30b87de6aa3", + "type": "raw_event" + }, + { + "id": "61512b1a-a94e-4cac-8bab-763588dbbddf", + "type": "raw_event" + }, + { + "id": "c0bf87f2-37c3-4895-90b0-9f97ac4b5c13", + "type": "raw_event" + }, + { + "id": "3856c7e7-f832-42ca-873b-096952599e29", + "type": "raw_event" + }, + { + "id": "485be998-0861-4d10-8a60-f66d27eb46a7", + "type": "raw_event" + }, + { + "id": "29cde194-bbd9-40c2-adba-5dcb1514f5fc", + "type": "raw_event" + } + ] + } + } + }, + { + "id": "681d713d-bcd6-4303-b082-b9f893e7d1d9", + "type": "container", + "attributes": { + "number": "CSNU8439129", + "seal_number": null, + "created_at": "2024-06-26T15:02:32Z", + "ref_numbers": [], + "pod_arrived_at": "2024-06-23T21:58:00Z", + "pod_discharged_at": "2024-06-26T04:30:00Z", + "final_destination_full_out_at": null, + "holds_at_pod_terminal": [], + "available_for_pickup": true, + "equipment_type": "dry", + "equipment_length": 40, + "equipment_height": "high_cube", + "weight_in_lbs": 40488, + "pod_full_out_at": null, + "empty_terminated_at": null, + "terminal_checked_at": "2024-06-26T18:47:47Z", + "fees_at_pod_terminal": [], + "pickup_lfd": "2024-07-01T07:00:00Z", + "pickup_appointment_at": "2024-06-28T15:00:00Z", + "pod_full_out_chassis_number": null, + "location_at_pod_terminal": "Grounded", + "pod_last_tracking_request_at": "2024-06-26T18:47:46Z", + "shipment_last_tracking_request_at": "2024-06-26T15:02:32Z", + "availability_known": true, + "pod_timezone": "America/Los_Angeles", + "final_destination_timezone": "America/Chicago", + "empty_terminated_timezone": "America/Chicago", + "pod_rail_carrier_scac": "BNSF", + "ind_rail_carrier_scac": "BNSF", + "pod_rail_loaded_at": null, + "pod_rail_departed_at": null, + "ind_eta_at": "2024-07-04T22:00:00Z", + "ind_ata_at": null, + "ind_rail_unloaded_at": null, + "ind_facility_lfd_on": null + }, + "relationships": { + "shipment": { + "data": { + "id": "edd626cf-b0b5-4679-8a6c-80c8e9fe7698", + "type": "shipment" + } + }, + "pickup_facility": { + "data": { + "id": "572b372f-21c7-4403-8fb0-948377c74642", + "type": "terminal" + } + }, + "pod_terminal": { + "data": { + "id": "eaa2580c-5f5b-4198-85e4-821145d62098", + "type": "terminal" + } + }, + "transport_events": { + "data": [ + { + "id": "9d19125a-2944-442c-8132-bf0d83670e5c", + "type": "transport_event" + }, + { + "id": "b2ff0b47-3151-41e2-8c46-7f95cdbe4167", + "type": "transport_event" + }, + { + "id": "e52feaa3-7a50-4570-a2ea-bf06f955ce23", + "type": "transport_event" + }, + { + "id": "c56d95e5-774f-432c-b6f4-c53967f07292", + "type": "transport_event" + }, + { + "id": "9b1d8b48-e870-46fa-bc46-10f9b30c64d4", + "type": "transport_event" + }, + { + "id": "3e1d3571-6b24-4ad2-a071-4e70d59af521", + "type": "transport_event" + }, + { + "id": "60f5eefe-13d5-4f85-9b65-d13b4c67115a", + "type": "transport_event" + } + ] + }, + "raw_events": { + "data": [ + { + "id": "2af51127-0971-4741-9b97-ea1b338e3a7c", + "type": "raw_event" + }, + { + "id": "4472ded6-eb69-4f21-8666-9a4f2342dfeb", + "type": "raw_event" + }, + { + "id": "ce238754-d5fd-4dc8-9f55-2ac6efdfbb5e", + "type": "raw_event" + }, + { + "id": "5cd3b5ef-843f-4f60-87ce-5beaeea86f7b", + "type": "raw_event" + }, + { + "id": "55291737-98b6-403c-9424-1605e6e01007", + "type": "raw_event" + }, + { + "id": "cdc55ea8-a075-45b3-9570-ade6fb2f0d94", + "type": "raw_event" + }, + { + "id": "c0ab5406-4e10-4fdc-85a4-e17ce8d956f5", + "type": "raw_event" + }, + { + "id": "965f0172-1c20-4342-a44a-7be0e594ff76", + "type": "raw_event" + }, + { + "id": "db178011-c795-42a4-9537-b4e77ffb4f98", + "type": "raw_event" + } + ] + } + } + } + ], + "meta": { + "size": 5, + "total": 59229 + }, + "links": { + "self": "https://api.terminal49.com/v2/containers?page[size]=5", + "current": "https://api.terminal49.com/v2/containers?page[number]=1&page[size]=5", + "next": "https://api.terminal49.com/v2/containers?page[number]=2&page[size]=5", + "last": "https://api.terminal49.com/v2/containers?page[number]=11846&page[size]=5" + } + } + } + } + } + } + } + }, + "operationId": "get-containers", + "description": "Returns a list of container. The containers are returned sorted by creation date, with the most recently refreshed containers appearing first.\n\nThis API will return all containers associated with the account.", + "parameters": [ + { + "schema": { + "type": "integer", + "default": 1 + }, + "in": "query", + "name": "page[number]" + }, + { + "schema": { + "type": "integer", + "default": 30 + }, + "in": "query", + "name": "page[size]", + "description": "" + }, + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "include", + "description": "Comma delimited list of relations to include" + }, + { + "schema": { + "type": "integer" + }, + "in": "query", + "name": "terminal_checked_before", + "description": "Number of seconds in which containers were refreshed" + } + ] + }, + "patch": { + "summary": "Edit a container", + "operationId": "patch-containers-id", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/container" + } + } + } + } + } + } + }, + "description": "Update a container", + "tags": [ + "Containers" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "type": "object", + "properties": { + "ref_numbers": { + "type": "array", + "items": { + "type": "string", + "example": "REF-12345" + } + } + } + } + } + } + } + } + } + } + } + } + }, + "/containers/{id}": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "id", + "in": "path", + "required": true + } + ], + "get": { + "summary": "Get a container", + "tags": [ + "Containers" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/container" + }, + "included": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/shipment" + }, + { + "$ref": "#/components/schemas/terminal" + }, + { + "$ref": "#/components/schemas/transport_event" + } + ] + } + } + } + }, + "examples": { + "Example Container": { + "value": { + "data": { + "id": "55a700e4-7005-45a9-92fd-1ff38641dbd9", + "type": "container", + "attributes": { + "number": "CAIU7432986", + "seal_number": null, + "created_at": "2024-06-26T15:05:21Z", + "ref_numbers": [], + "pod_arrived_at": null, + "pod_discharged_at": "2024-06-22T04:00:00Z", + "final_destination_full_out_at": null, + "holds_at_pod_terminal": [], + "available_for_pickup": true, + "equipment_type": "dry", + "equipment_length": 40, + "equipment_height": "high_cube", + "weight_in_lbs": null, + "pod_full_out_at": null, + "empty_terminated_at": null, + "terminal_checked_at": "2024-06-26T17:51:12Z", + "fees_at_pod_terminal": [], + "pickup_lfd": "2024-07-07T04:00:00Z", + "pickup_appointment_at": null, + "pod_full_out_chassis_number": null, + "location_at_pod_terminal": "Yard - Y0709A", + "pod_last_tracking_request_at": "2024-06-26T17:51:12Z", + "shipment_last_tracking_request_at": "2024-06-26T15:05:20Z", + "availability_known": true, + "pod_timezone": "America/New_York", + "final_destination_timezone": "US/Eastern", + "empty_terminated_timezone": "US/Eastern", + "pod_rail_carrier_scac": "CSXT", + "ind_rail_carrier_scac": "CSXT", + "pod_rail_loaded_at": null, + "pod_rail_departed_at": null, + "ind_eta_at": null, + "ind_ata_at": null, + "ind_rail_unloaded_at": null, + "ind_facility_lfd_on": null + }, + "relationships": { + "shipment": { + "data": { + "id": "02b1bd6f-407c-45bb-8645-06e7ee34e7e3", + "type": "shipment" + } + }, + "pickup_facility": { + "data": null + }, + "pod_terminal": { + "data": { + "id": "b859f5c3-8515-41da-bf20-39c0a5ada887", + "type": "terminal" + } + }, + "transport_events": { + "data": [ + { + "id": "45b542cb-332b-4684-b915-42e3a0759823", + "type": "transport_event" + }, + { + "id": "174ed528-a1a9-4002-aef0-f2c9369199da", + "type": "transport_event" + }, + { + "id": "7a2f30a6-a756-4c14-9477-fbfc1c7fe2f8", + "type": "transport_event" + }, + { + "id": "e7365004-175a-46e8-96cd-dbed0f3daf21", + "type": "transport_event" + }, + { + "id": "7c567bf3-7f01-4a3d-a176-eaa1f7165585", + "type": "transport_event" + } + ] + }, + "raw_events": { + "data": [ + { + "id": "2956f71c-bfb9-4e49-b9e2-1b4d53c74cac", + "type": "raw_event" + }, + { + "id": "391e0eda-65b5-4fc3-a53d-25ecd9570259", + "type": "raw_event" + }, + { + "id": "74810c04-6c8a-4194-8cff-52936584a965", + "type": "raw_event" + }, + { + "id": "4b1500e2-b23b-4896-87bd-c38b1d16f385", + "type": "raw_event" + }, + { + "id": "8b9a7d88-720a-4304-8c1e-a3336e39f481", + "type": "raw_event" + }, + { + "id": "bf1f59c5-5dd8-4013-87f9-d7056bc87114", + "type": "raw_event" + } + ] + } + } + }, + "links": { + "self": "https://api.terminal49.com/v2/containers/55a700e4-7005-45a9-92fd-1ff38641dbd9" + } + } + } + } + } + } + } + }, + "operationId": "get-containers-id", + "description": "Retrieves the details of a container.", + "parameters": [ + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "include", + "description": "Comma delimited list of relations to include" + } + ] + } + }, + "/containers/{id}/raw_events": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "id", + "in": "path", + "required": true + } + ], + "get": { + "summary": "Get a container's raw events", + "tags": [ + "Containers" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/raw_event" + } + } + } + }, + "examples": { + "Example Raw Events": { + "value": { + "data": [ + { + "id": "ca6b760f-13e9-4bf6-ab49-3cf2e40757fb", + "type": "raw_event", + "attributes": { + "timestamp": "2020-03-03T00:00:00Z", + "estimated": false, + "actual_on": "2020-03-03", + "estimated_at": null, + "actual_at": null, + "event": "empty_out", + "index": 0, + "original_event": "Truck Gate out empty", + "created_at": "2020-04-18T00:18:27Z", + "voyage_number": null, + "location_name": "Oakland", + "location_locode": null, + "vessel_name": null, + "vessel_imo": null, + "timezone": null + }, + "relationships": { + "location": { + "data": null + }, + "vessel": { + "data": null + } + } + }, + { + "id": "bcdfc796-0570-4c85-9336-d6c7d0da02d2", + "type": "raw_event", + "attributes": { + "timestamp": "2020-03-09T00:00:00Z", + "estimated": false, + "actual_on": "2020-03-09", + "estimated_at": null, + "actual_at": null, + "event": "full_in", + "index": 1, + "original_event": "Truck Arrival in", + "created_at": "2020-04-18T00:18:27Z", + "voyage_number": null, + "location_name": null, + "location_locode": null, + "vessel_name": null, + "vessel_imo": null, + "timezone": null + }, + "relationships": { + "location": { + "data": null + }, + "vessel": { + "data": null + } + } + }, + { + "id": "a4ff01b0-b374-4123-ae65-3dc0c7ea41ea", + "type": "raw_event", + "attributes": { + "timestamp": "2020-03-14T00:00:00Z", + "estimated": false, + "actual_on": "2020-03-14", + "estimated_at": null, + "actual_at": null, + "event": "vessel_loaded", + "index": 2, + "original_event": "Vessel Loaded", + "created_at": "2020-04-18T00:18:27Z", + "voyage_number": "FA009R", + "location_name": null, + "location_locode": null, + "vessel_name": "MSC FAITH", + "vessel_imo": null, + "timezone": null + }, + "relationships": { + "location": { + "data": null + }, + "vessel": { + "data": { + "id": "4b473d0e-7073-4171-8b5b-15e71e9e13cc", + "type": "vessel" + } + } + } + }, + { + "id": "ca5862ef-6e27-4245-a281-0cec6bbe1fb7", + "type": "raw_event", + "attributes": { + "timestamp": "2020-03-15T00:00:00Z", + "estimated": false, + "actual_on": "2020-03-15", + "estimated_at": null, + "actual_at": null, + "event": "vessel_departed", + "index": 3, + "original_event": "Vessel departed", + "created_at": "2020-04-18T00:18:27Z", + "voyage_number": "FA009R", + "location_name": null, + "location_locode": null, + "vessel_name": "MSC FAITH", + "vessel_imo": null, + "timezone": null + }, + "relationships": { + "location": { + "data": null + }, + "vessel": { + "data": { + "id": "4b473d0e-7073-4171-8b5b-15e71e9e13cc", + "type": "vessel" + } + } + } + }, + { + "id": "f47a903e-e6d1-41c5-aec6-8401b2abf297", + "type": "raw_event", + "attributes": { + "timestamp": "2020-03-25T00:00:00Z", + "estimated": false, + "actual_on": "2020-03-25", + "estimated_at": null, + "actual_at": null, + "event": "transshipment_arrived", + "index": 4, + "original_event": "Vessel arrived", + "created_at": "2020-04-18T00:18:27Z", + "voyage_number": "FA009R", + "location_name": null, + "location_locode": null, + "vessel_name": "MSC FAITH", + "vessel_imo": null, + "timezone": null + }, + "relationships": { + "location": { + "data": null + }, + "vessel": { + "data": { + "id": "4b473d0e-7073-4171-8b5b-15e71e9e13cc", + "type": "vessel" + } + } + } + }, + { + "id": "72a1a13b-a2e0-4ac0-851d-eec41e9e9087", + "type": "raw_event", + "attributes": { + "timestamp": "2020-03-25T00:00:00Z", + "estimated": false, + "actual_on": "2020-03-25", + "estimated_at": null, + "actual_at": null, + "event": "transshipment_discharged", + "index": 5, + "original_event": "Vessel Discharged", + "created_at": "2020-04-18T00:18:27Z", + "voyage_number": "FA009R", + "location_name": null, + "location_locode": null, + "vessel_name": "MSC FAITH", + "vessel_imo": null, + "timezone": null + }, + "relationships": { + "location": { + "data": null + }, + "vessel": { + "data": { + "id": "4b473d0e-7073-4171-8b5b-15e71e9e13cc", + "type": "vessel" + } + } + } + }, + { + "id": "cd91f0cf-ee73-4c47-b99f-63245cb5bc96", + "type": "raw_event", + "attributes": { + "timestamp": "2020-04-07T00:00:00Z", + "estimated": false, + "actual_on": "2020-04-07", + "estimated_at": null, + "actual_at": null, + "event": "transshipment_loaded", + "index": 6, + "original_event": "Vessel Loaded", + "created_at": "2020-04-18T00:18:27Z", + "voyage_number": "15W10", + "location_name": null, + "location_locode": null, + "vessel_name": "SINGAPORE EXPRESS", + "vessel_imo": null, + "timezone": null + }, + "relationships": { + "location": { + "data": null + }, + "vessel": { + "data": { + "id": "345c05ab-4217-4ffe-a1a4-6c03b9ad2b36", + "type": "vessel" + } + } + } + }, + { + "id": "561dbb7e-c3ab-4e63-b09b-957878b1425f", + "type": "raw_event", + "attributes": { + "timestamp": "2020-04-07T00:00:00Z", + "estimated": false, + "actual_on": "2020-04-07", + "estimated_at": null, + "actual_at": null, + "event": "transshipment_departed", + "index": 7, + "original_event": "Vessel departed", + "created_at": "2020-04-18T00:18:27Z", + "voyage_number": "15W10", + "location_name": null, + "location_locode": null, + "vessel_name": "SINGAPORE EXPRESS", + "vessel_imo": null, + "timezone": null + }, + "relationships": { + "location": { + "data": null + }, + "vessel": { + "data": { + "id": "345c05ab-4217-4ffe-a1a4-6c03b9ad2b36", + "type": "vessel" + } + } + } + }, + { + "id": "551711a6-62ad-4205-8da2-00e0c0cbd2db", + "type": "raw_event", + "attributes": { + "timestamp": "2020-04-12T00:00:00Z", + "estimated": false, + "actual_on": "2020-04-12", + "estimated_at": null, + "actual_at": null, + "event": "vessel_arrived", + "index": 8, + "original_event": "Vessel arrived", + "created_at": "2020-04-18T00:18:27Z", + "voyage_number": "15W10", + "location_name": null, + "location_locode": null, + "vessel_name": "SINGAPORE EXPRESS", + "vessel_imo": null, + "timezone": null + }, + "relationships": { + "location": { + "data": null + }, + "vessel": { + "data": { + "id": "345c05ab-4217-4ffe-a1a4-6c03b9ad2b36", + "type": "vessel" + } + } + } + }, + { + "id": "f4027470-75ca-4e2a-b4f0-47654a25ac48", + "type": "raw_event", + "attributes": { + "timestamp": "2020-04-13T00:00:00Z", + "estimated": false, + "actual_on": "2020-04-13", + "estimated_at": null, + "actual_at": null, + "event": "vessel_discharged", + "index": 9, + "original_event": "Vessel Discharged", + "created_at": "2020-04-18T00:18:27Z", + "voyage_number": "15W10", + "location_name": null, + "location_locode": null, + "vessel_name": "SINGAPORE EXPRESS", + "vessel_imo": null, + "timezone": null + }, + "relationships": { + "location": { + "data": null + }, + "vessel": { + "data": { + "id": "345c05ab-4217-4ffe-a1a4-6c03b9ad2b36", + "type": "vessel" + } + } + } + }, + { + "id": "50f11e4f-411e-48e2-8141-64226500df9c", + "type": "raw_event", + "attributes": { + "timestamp": "2020-04-14T00:00:00Z", + "estimated": false, + "actual_on": "2020-04-14", + "estimated_at": null, + "actual_at": null, + "event": "full_out", + "index": 10, + "original_event": "Truck Departure from", + "created_at": "2020-04-18T00:18:27Z", + "voyage_number": null, + "location_name": null, + "location_locode": null, + "vessel_name": null, + "vessel_imo": null, + "timezone": null + }, + "relationships": { + "location": { + "data": null + }, + "vessel": { + "data": null + } + } + }, + { + "id": "49aea23c-b8c5-4a97-b133-f7a9723fa1b4", + "type": "raw_event", + "attributes": { + "timestamp": "2020-04-15T00:00:00Z", + "estimated": false, + "actual_on": "2020-04-15", + "estimated_at": null, + "actual_at": null, + "event": "empty_in", + "index": 11, + "original_event": "Truck Gate in empty", + "created_at": "2020-04-18T00:18:27Z", + "voyage_number": null, + "location_name": null, + "location_locode": null, + "vessel_name": null, + "vessel_imo": null, + "timezone": null + }, + "relationships": { + "location": { + "data": null + }, + "vessel": { + "data": null + } + } + } + ], + "included": [ + { + "id": "4b473d0e-7073-4171-8b5b-15e71e9e13cc", + "type": "vessel", + "attributes": { + "name": "MSC FAITH", + "imo": null, + "mmsi": "636019213", + "latitude": 70.22625823437389, + "longitude": 45.06279126658865, + "nautical_speed_knots": 100, + "navigational_heading_degrees": 1, + "position_timestamp": "2023-06-05T19:46:18Z" + } + }, + { + "id": "345c05ab-4217-4ffe-a1a4-6c03b9ad2b36", + "type": "vessel", + "attributes": { + "name": "SINGAPORE EXPRESS", + "imo": null, + "mmsi": "477300500", + "latitude": 70.22625823437389, + "longitude": 45.06279126658865, + "nautical_speed_knots": 100, + "navigational_heading_degrees": 1, + "position_timestamp": "2023-06-05T19:46:18Z" + } + } + ] + } + } + } + } + } + } + }, + "operationId": "get-containers-id-raw_events", + "description": "#### Deprecation warning\nThe `raw_events` endpoint is provided as-is.\n\n For past events we recommend consuming `transport_events`.\n\n---\nGet a list of past and future (estimated) milestones for a container as reported by the carrier. Some of the data is normalized even though the API is called raw_events. \n\nNormalized attributes: `event` and `timestamp` timestamp. Not all of the `event` values have been normalized. You can expect the the events related to container movements to be normalized but there are cases where events are not normalized. \n\nFor past historical events we recommend consuming `transport_events`. Although there are fewer events here those events go through additional vetting and normalization to avoid false positives and get you correct data.", + "deprecated": true + } + }, + "/containers/{id}/transport_events": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "id", + "in": "path", + "required": true + } + ], + "get": { + "summary": "Get a container's transport events", + "tags": [ + "Containers" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/transport_event" + } + }, + "included": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/shipment" + }, + { + "$ref": "#/components/schemas/container" + }, + { + "$ref": "#/components/schemas/port" + }, + { + "$ref": "#/components/schemas/metro_area" + }, + { + "$ref": "#/components/schemas/terminal" + }, + { + "$ref": "#/components/schemas/rail_terminal" + }, + { + "$ref": "#/components/schemas/vessel" + } + ] + } + }, + "links": { + "$ref": "#/components/schemas/links" + }, + "meta": { + "$ref": "#/components/schemas/meta" + } + } + }, + "examples": { + "Example transport events": { + "value": { + "data": [ + { + "id": "efc3f3c1-cdc2-4a7d-a176-762ddec107b8", + "type": "transport_event", + "attributes": { + "event": "container.transport.vessel_loaded", + "created_at": "2021-01-05T08:41:12Z", + "voyage_number": "15W10", + "timestamp": null, + "location_locode": "CLSAI", + "timezone": "America/Santiago" + }, + "relationships": { + "shipment": { + "data": { + "id": "06264731-503e-498e-bc76-f90b87b31562", + "type": "shipment" + } + }, + "container": { + "data": { + "id": "eeafd337-72b5-4e5c-87cb-9ef83fa99cf4", + "type": "container" + } + }, + "vessel": { + "data": { + "id": "345c05ab-4217-4ffe-a1a4-6c03b9ad2b36", + "type": "vessel" + } + }, + "location": { + "data": { + "id": "0ad2cf2b-e694-4ccc-9cd2-40af0d1fa1b5", + "type": "port" + } + }, + "terminal": { + "data": null + } + } + }, + { + "id": "951058bd-2c3b-4bcc-94e1-9be2526b9687", + "type": "transport_event", + "attributes": { + "event": "container.transport.vessel_departed", + "created_at": "2021-01-05T08:41:11Z", + "voyage_number": "15W10", + "timestamp": null, + "location_locode": "CLSAI", + "timezone": "America/Santiago" + }, + "relationships": { + "shipment": { + "data": { + "id": "06264731-503e-498e-bc76-f90b87b31562", + "type": "shipment" + } + }, + "container": { + "data": { + "id": "eeafd337-72b5-4e5c-87cb-9ef83fa99cf4", + "type": "container" + } + }, + "vessel": { + "data": { + "id": "345c05ab-4217-4ffe-a1a4-6c03b9ad2b36", + "type": "vessel" + } + }, + "location": { + "data": { + "id": "0ad2cf2b-e694-4ccc-9cd2-40af0d1fa1b5", + "type": "port" + } + }, + "terminal": { + "data": null + } + } + }, + { + "id": "69af6795-56c2-4157-9a87-afd761cc85a0", + "type": "transport_event", + "attributes": { + "event": "container.transport.full_out", + "created_at": "2020-05-14T00:05:41Z", + "voyage_number": null, + "timestamp": "2020-04-14T00:00:00Z", + "location_locode": "USOAK", + "timezone": "America/Los_Angeles" + }, + "relationships": { + "shipment": { + "data": { + "id": "06264731-503e-498e-bc76-f90b87b31562", + "type": "shipment" + } + }, + "container": { + "data": { + "id": "eeafd337-72b5-4e5c-87cb-9ef83fa99cf4", + "type": "container" + } + }, + "vessel": { + "data": null + }, + "location": { + "data": { + "id": "42d1ba3a-f4b8-431d-a6fe-49fd748a59e7", + "type": "port" + } + }, + "terminal": { + "data": null + } + } + }, + { + "id": "68c3c29a-504a-4dbb-ad27-7194ef42d484", + "type": "transport_event", + "attributes": { + "event": "container.transport.vessel_discharged", + "created_at": "2020-05-14T00:05:41Z", + "voyage_number": "15W10", + "timestamp": "2020-04-13T00:00:00Z", + "location_locode": "USOAK", + "timezone": "America/Los_Angeles" + }, + "relationships": { + "shipment": { + "data": { + "id": "06264731-503e-498e-bc76-f90b87b31562", + "type": "shipment" + } + }, + "container": { + "data": { + "id": "eeafd337-72b5-4e5c-87cb-9ef83fa99cf4", + "type": "container" + } + }, + "vessel": { + "data": { + "id": "345c05ab-4217-4ffe-a1a4-6c03b9ad2b36", + "type": "vessel" + } + }, + "location": { + "data": { + "id": "42d1ba3a-f4b8-431d-a6fe-49fd748a59e7", + "type": "port" + } + }, + "terminal": { + "data": { + "id": "3e550f0e-ac2a-48fb-b242-5be45ecf2c78", + "type": "terminal" + } + } + } + }, + { + "id": "03349405-a9be-4f3e-abde-28f2cb3922bd", + "type": "transport_event", + "attributes": { + "event": "container.transport.vessel_arrived", + "created_at": "2020-05-14T00:05:41Z", + "voyage_number": "15W10", + "timestamp": "2020-04-13T01:24:00Z", + "location_locode": "USOAK", + "timezone": "America/Los_Angeles" + }, + "relationships": { + "shipment": { + "data": { + "id": "06264731-503e-498e-bc76-f90b87b31562", + "type": "shipment" + } + }, + "container": { + "data": { + "id": "eeafd337-72b5-4e5c-87cb-9ef83fa99cf4", + "type": "container" + } + }, + "vessel": { + "data": { + "id": "345c05ab-4217-4ffe-a1a4-6c03b9ad2b36", + "type": "vessel" + } + }, + "location": { + "data": { + "id": "42d1ba3a-f4b8-431d-a6fe-49fd748a59e7", + "type": "port" + } + }, + "terminal": { + "data": { + "id": "3e550f0e-ac2a-48fb-b242-5be45ecf2c78", + "type": "terminal" + } + } + } + }, + { + "id": "ba9f85b4-658d-4f23-9308-635964df8037", + "type": "transport_event", + "attributes": { + "event": "container.transport.empty_in", + "created_at": "2020-05-14T00:05:42Z", + "voyage_number": null, + "timestamp": "2020-04-15T00:00:00Z", + "location_locode": null, + "timezone": null + }, + "relationships": { + "shipment": { + "data": { + "id": "06264731-503e-498e-bc76-f90b87b31562", + "type": "shipment" + } + }, + "container": { + "data": { + "id": "eeafd337-72b5-4e5c-87cb-9ef83fa99cf4", + "type": "container" + } + }, + "vessel": { + "data": null + }, + "location": { + "data": null + }, + "terminal": { + "data": null + } + } + } + ], + "links": { + "self": "https://api.terminal49.com/v2/containers/eeafd337-72b5-4e5c-87cb-9ef83fa99cf4/transport_events", + "current": "https://api.terminal49.com/v2/containers/eeafd337-72b5-4e5c-87cb-9ef83fa99cf4/transport_events?page[number]=1" + } + } + } + } + } + } + } + }, + "operationId": "get-containers-id-transport_events", + "description": "Get a list of past transport events (canonical) for a container. All data has been normalized across all carriers. These are a verified subset of the raw events may also be sent as Webhook Notifications to a webhook endpoint.\n\nThis does not provide any estimated future events. See `container/:id/raw_events` endpoint for that. ", + "parameters": [ + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "include", + "description": "Comma delimited list of relations to include" + } + ] + } + }, + "/shipping_lines": { + "get": { + "summary": "Shipping Lines", + "tags": [ + "Shipping Lines" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/shipping_line" + } + }, + "links": { + "$ref": "#/components/schemas/links" + } + } + } + } + } + } + }, + "operationId": "get-shipping_lines", + "description": "Return a list of shipping lines supported by Terminal49. \nN.B. There is no pagination for this endpoint." + } + }, + "/shipping_lines/{id}": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "id", + "in": "path", + "required": true + } + ], + "get": { + "summary": "Get a single shipping line", + "tags": [ + "Shipping Lines" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/shipping_line" + } + } + } + } + } + } + }, + "operationId": "get-shipping_lines-id", + "description": "Return the details of a single shipping line." + } + }, + "/metro_areas/{id}": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "id", + "in": "path", + "required": true + } + ], + "get": { + "summary": "Get a metro area using the un/locode or the id", + "tags": [ + "Metro Areas" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/metro_area" + } + } + } + } + } + } + }, + "operationId": "get-metro-area-id", + "description": "Return the details of a single metro area." + } + }, + "/ports/{id}": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "id", + "in": "path", + "required": true + } + ], + "get": { + "summary": "Get a port using the locode or the id", + "tags": [ + "Ports" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/port" + } + } + } + } + } + } + }, + "operationId": "get-port-id", + "description": "Return the details of a single port." + } + }, + "/vessels/{id}": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "id", + "in": "path", + "required": true + } + ], + "get": { + "summary": "Get a vessel using the id", + "tags": [ + "Vessels" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/vessel" + } + } + } + } + } + } + }, + "operationId": "get-vessels-id", + "description": "Returns a vessel by it's given identifier" + } + }, + "/vessels/{imo}": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "imo", + "in": "path", + "required": true + } + ], + "get": { + "summary": "Get a vessel using the imo", + "tags": [ + "Vessels" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/vessel" + } + } + } + } + } + } + }, + "operationId": "get-vessels-imo", + "description": "Returns a vessel by the given IMO number.", + "x-internal": true + } + }, + "/terminals/{id}": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "id", + "in": "path", + "required": true + } + ], + "get": { + "summary": "Get a terminal using the id", + "tags": [ + "Terminals" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/terminal" + } + } + } + } + } + } + }, + "operationId": "get-terminal-id", + "description": "Return the details of a single terminal." + } + }, + "/parties": { + "get": { + "description": "Get a list of parties", + "operationId": "list-parties", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/party" + } + }, + "links": { + "$ref": "#/components/schemas/links" + }, + "meta": { + "$ref": "#/components/schemas/meta" + } + } + } + } + } + } + }, + "parameters": [ + { + "schema": { + "type": "integer", + "default": 1 + }, + "in": "query", + "name": "page[number]" + }, + { + "schema": { + "type": "integer", + "default": 25 + }, + "in": "query", + "name": "page[size]", + "description": "" + } + ], + "tags": [ + "Parties" + ] + }, + "post": { + "description": "Creates a new party", + "operationId": "post-party", + "responses": { + "201": { + "description": "Party Created", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/party" + }, + "links": { + "$ref": "#/components/schemas/link-self" + } + } + }, + "examples": { + "New Party": { + "value": { + "data": { + "id": "ba4cb904-827f-4038-8e31-1e92b3356218", + "type": "party", + "attributes": { + "company_name": "COMPANY NAME" + } + }, + "links": { + "self": "/v2/parties/ba4cb904-827f-4038-8e31-1e92b3356218" + } + } + } + } + } + }, + "headers": {} + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "errors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "examples": { + "Error Examples": { + "value": { + "errors": [ + { + "status": "422", + "source": { + "pointer": "/data/attributes/company_name" + }, + "title": "Unprocessable Entity", + "detail": "Company name can't be blank" + }, + { + "status": "422", + "source": { + "pointer": "/data/attributes/customer" + }, + "title": "Unprocessable Entity", + "detail": "'XXXX' already exists in Account" + } + ] + } + } + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "required": [ + "attributes" + ], + "properties": { + "attributes": { + "type": "object", + "properties": { + "company_name": { + "type": "string", + "example": "COMPANY NAME", + "description": "The name of the company" + } + } + } + } + } + } + } + } + } + }, + "tags": [ + "Parties" + ] + } + }, + "/parties/{id}": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "id", + "in": "path", + "required": true + } + ], + "get": { + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/party" + }, + "links": { + "$ref": "#/components/schemas/link-self" + } + } + } + } + } + } + }, + "operationId": "get-parties-id", + "description": "Returns a party by it's given identifier", + "tags": [ + "Parties" + ] + }, + "patch": { + "description": "Updates a party", + "operationId": "edit-party", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/party" + }, + "links": { + "$ref": "#/components/schemas/link-self" + } + } + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "errors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "examples": { + "Error Examples": { + "value": { + "errors": [ + { + "status": "422", + "source": { + "pointer": "/data/attributes/company_name" + }, + "title": "Unprocessable Entity", + "detail": "Company name can't be blank" + }, + { + "status": "422", + "source": { + "pointer": "/data/attributes/customer" + }, + "title": "Unprocessable Entity", + "detail": "'XXXX' already exists in Account" + } + ] + } + } + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "required": [ + "attributes" + ], + "properties": { + "attributes": { + "type": "object", + "properties": { + "company_name": { + "type": "string", + "example": "COMPANY NAME", + "description": "The name of the company" + } + } + } + } + } + } + } + } + } + }, + "tags": [ + "Parties" + ] + } + } + }, + "x-tagGroups": [ + { + "name": "End Points", + "tags": [ + "Shipments", + "Containers", + "Tracking Requests", + "Webhooks", + "Webhook Notifications", + "Metro Areas" + ] + } + ], + "components": { + "schemas": { + "shipment": { + "title": "Shipment model", + "type": "object", + "x-examples": {}, + "description": "", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "relationships": { + "type": "object", + "properties": { + "destination": { + "type": "object", + "properties": { + "data": { + "type": "object", + "nullable": true, + "properties": { + "type": { + "type": "string", + "enum": [ + "port", + "metro_area" + ] + }, + "id": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "type", + "id" + ] + } + } + }, + "port_of_lading": { + "type": "object", + "properties": { + "data": { + "type": "object", + "nullable": true, + "properties": { + "type": { + "type": "string", + "enum": [ + "port" + ] + }, + "id": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "type", + "id" + ] + } + } + }, + "containers": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "container" + ] + }, + "id": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "type", + "id" + ] + } + } + } + }, + "port_of_discharge": { + "type": "object", + "properties": { + "data": { + "type": "object", + "nullable": true, + "properties": { + "type": { + "type": "string", + "enum": [ + "port" + ] + }, + "id": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "type", + "id" + ] + } + } + }, + "pod_terminal": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "terminal" + ] + }, + "id": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "type", + "id" + ] + } + } + }, + "destination_terminal": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "terminal", + "rail_terminal" + ] + }, + "id": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "type", + "id" + ] + } + } + }, + "line_tracking_stopped_by_user": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "user" + ] + }, + "id": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "type", + "id" + ] + } + } + } + } + }, + "attributes": { + "type": "object", + "properties": { + "bill_of_lading_number": { + "type": "string" + }, + "normalized_number": { + "type": "string", + "description": "The normalized version of the shipment number used for querying the carrier" + }, + "ref_numbers": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "port_of_lading_locode": { + "type": "string", + "description": "UN/LOCODE", + "nullable": true + }, + "port_of_lading_name": { + "type": "string", + "nullable": true + }, + "port_of_discharge_locode": { + "type": "string", + "description": "UN/LOCODE", + "nullable": true + }, + "port_of_discharge_name": { + "type": "string", + "nullable": true + }, + "destination_locode": { + "type": "string", + "description": "UN/LOCODE", + "nullable": true + }, + "destination_name": { + "type": "string", + "nullable": true + }, + "shipping_line_scac": { + "type": "string" + }, + "shipping_line_name": { + "type": "string" + }, + "shipping_line_short_name": { + "type": "string" + }, + "customer_name": { + "type": "string", + "nullable": true + }, + "pod_vessel_name": { + "type": "string", + "nullable": true + }, + "pod_vessel_imo": { + "type": "string", + "nullable": true + }, + "pod_voyage_number": { + "type": "string", + "nullable": true + }, + "pol_etd_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "pol_atd_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "pod_eta_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "pod_original_eta_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "pod_ata_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "destination_eta_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "destination_ata_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "pol_timezone": { + "type": "string", + "description": "IANA tz", + "nullable": true + }, + "pod_timezone": { + "type": "string", + "description": "IANA tz", + "nullable": true + }, + "destination_timezone": { + "type": "string", + "description": "IANA tz", + "nullable": true + }, + "line_tracking_last_attempted_at": { + "type": "string", + "format": "date-time", + "description": "When Terminal49 last tried to update the shipment status from the shipping line", + "nullable": true + }, + "line_tracking_last_succeeded_at": { + "type": "string", + "format": "date-time", + "description": "When Terminal49 last successfully updated the shipment status from the shipping line", + "nullable": true + }, + "line_tracking_stopped_at": { + "type": "string", + "format": "date-time", + "description": "When Terminal49 stopped checking at the shipping line", + "nullable": true + }, + "line_tracking_stopped_reason": { + "type": "string", + "enum": [ +{ + "openapi": "3.0.0", + "info": { + "title": "Terminal49 API Reference", + "version": "0.2.0", + "contact": { + "name": "Terminal49 API support", + "url": "https://www.terminal49.com", + "email": "support@terminal49.com" + }, + "description": "The Terminal 49 API offers a convenient way to programmatically track your shipments from origin to destination.", + "x-label": "Beta", + "termsOfService": "https://www.terminal49.com/terms" + }, + "servers": [ + { + "url": "https://api.terminal49.com/v2", + "description": "Production" + } + ], + "paths": { + "/shipments": { + "get": { + "summary": "List shipments", + "tags": [ + "Shipments" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/shipment" + } + }, + "included": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/container" + }, + { + "$ref": "#/components/schemas/port" + }, + { + "$ref": "#/components/schemas/terminal" + } + ] + } + }, + "links": { + "$ref": "#/components/schemas/links" + }, + "meta": { + "$ref": "#/components/schemas/meta" + } + } + }, + "examples": { + "En-route to NY with inland move": { + "value": { + "data": [ + { + "id": "62738624-7032-4a50-892e-c55826228c25", + "type": "shipment", + "attributes": { + "created_at": "2024-06-26T17:28:59Z", + "ref_numbers": [], + "tags": [], + "bill_of_lading_number": "OOLU2148468620", + "normalized_number": "2148468620", + "shipping_line_scac": "OOLU", + "shipping_line_name": "Orient Overseas Container Line", + "shipping_line_short_name": "OOCL", + "customer_name": "Sodor Steamworks", + "port_of_lading_locode": "CNNBG", + "port_of_lading_name": "Ningbo", + "port_of_discharge_locode": "USLAX", + "port_of_discharge_name": "Los Angeles", + "pod_vessel_name": "EVER FORWARD", + "pod_vessel_imo": "9850551", + "pod_voyage_number": "1119E", + "destination_locode": "USCLE", + "destination_name": "Cleveland", + "destination_timezone": "America/New_York", + "destination_ata_at": null, + "destination_eta_at": "2024-07-02T08:00:00Z", + "pol_etd_at": null, + "pol_atd_at": "2024-06-09T03:42:00Z", + "pol_timezone": "Asia/Shanghai", + "pod_eta_at": null, + "pod_original_eta_at": null, + "pod_ata_at": "2024-06-22T13:36:00Z", + "pod_timezone": "America/Los_Angeles", + "line_tracking_last_attempted_at": "2024-06-26T17:28:59Z", + "line_tracking_last_succeeded_at": "2024-06-26T17:28:59Z", + "line_tracking_stopped_at": null, + "line_tracking_stopped_reason": null + }, + "links": { + "self": "/v2/shipments/62738624-7032-4a50-892e-c55826228c25" + }, + "relationships": { + "port_of_lading": { + "data": { + "id": "2a90c27b-c2ee-45e2-892f-5c695d74e2d0", + "type": "port" + } + }, + "port_of_discharge": { + "data": { + "id": "47b27584-4ec9-4e2f-95e1-7a42928cc40c", + "type": "port" + } + }, + "pod_terminal": { + "data": { + "id": "42c53b13-0d29-4fa6-8663-7343a56319f1", + "type": "terminal" + } + }, + "destination": { + "data": { + "id": "3b1cc325-ffe9-400a-82ce-f5c4891af382", + "type": "port" + } + }, + "destination_terminal": { + "data": { + "id": "ce22669e-14b2-4501-b782-f0a360f07cd0", + "type": "terminal" + } + }, + "line_tracking_stopped_by_user": { + "data": null + }, + "containers": { + "data": [ + { + "id": "7aefc29e-0898-4825-8376-4f998b51d033", + "type": "container" + } + ] + } + } + }, + { + "id": "baaa725e-aa0e-4937-ac78-54d9e2e8621e", + "type": "shipment", + "attributes": { + "created_at": "2024-06-26T16:47:42Z", + "ref_numbers": [], + "tags": [], + "bill_of_lading_number": "HDMUTAOM72244900", + "normalized_number": "TAOM72244900", + "shipping_line_scac": "HDMU", + "shipping_line_name": "Hyundai Merchant Marine", + "shipping_line_short_name": "Hyundai", + "customer_name": "Sodor Steamworks", + "port_of_lading_locode": "CNQDG", + "port_of_lading_name": "Qingdao", + "port_of_discharge_locode": "USSAV", + "port_of_discharge_name": "Savannah", + "pod_vessel_name": "UMM SALAL", + "pod_vessel_imo": "9525857", + "pod_voyage_number": "0038E", + "destination_locode": "USROQ", + "destination_name": "Rossville", + "destination_timezone": "America/Chicago", + "destination_ata_at": null, + "destination_eta_at": "2024-06-30T07:05:00Z", + "pol_etd_at": null, + "pol_atd_at": "2024-05-07T03:01:00Z", + "pol_timezone": "Asia/Shanghai", + "pod_eta_at": null, + "pod_original_eta_at": null, + "pod_ata_at": "2024-06-20T21:27:00Z", + "pod_timezone": "America/New_York", + "line_tracking_last_attempted_at": "2024-06-26T16:48:04Z", + "line_tracking_last_succeeded_at": "2024-06-26T16:48:04Z", + "line_tracking_stopped_at": null, + "line_tracking_stopped_reason": null + }, + "links": { + "self": "/v2/shipments/baaa725e-aa0e-4937-ac78-54d9e2e8621e" + }, + "relationships": { + "port_of_lading": { + "data": { + "id": "0ccbe8af-c8d0-4abd-a842-3bfad1d82024", + "type": "port" + } + }, + "port_of_discharge": { + "data": { + "id": "6129528d-846e-4571-ae16-b5328a4285ab", + "type": "port" + } + }, + "pod_terminal": { + "data": { + "id": "a243bdf8-0da3-4056-a6a7-05fe8ab43999", + "type": "terminal" + } + }, + "destination": { + "data": { + "id": "87ca3f37-e4d1-46eb-9eb1-6b5ffafde95d", + "type": "port" + } + }, + "destination_terminal": { + "data": null + }, + "line_tracking_stopped_by_user": { + "data": null + }, + "containers": { + "data": [ + { + "id": "772cd872-9677-4c68-9b7a-4e9e843b00e2", + "type": "container" + }, + { + "id": "52efc544-0de1-452f-bcf8-0290a6ce5c11", + "type": "container" + }, + { + "id": "3107692e-61ad-4b4c-b3d4-78348b2e37ff", + "type": "container" + } + ] + } + } + }, + { + "id": "7721a48c-5e93-43c9-9f5f-5be10a87fdde", + "type": "shipment", + "attributes": { + "created_at": "2024-06-26T16:28:39Z", + "ref_numbers": [], + "tags": [], + "bill_of_lading_number": "OOLU2738424980", + "normalized_number": "2738424980", + "shipping_line_scac": "OOLU", + "shipping_line_name": "Orient Overseas Container Line", + "shipping_line_short_name": "OOCL", + "customer_name": "Sodor Steamworks", + "port_of_lading_locode": "ITSPE", + "port_of_lading_name": "La Spezia", + "port_of_discharge_locode": "USSAV", + "port_of_discharge_name": "Savannah", + "pod_vessel_name": "OOCL GUANGZHOU", + "pod_vessel_imo": "9404869", + "pod_voyage_number": "162E", + "destination_locode": "USATL", + "destination_name": "Atlanta", + "destination_timezone": "America/New_York", + "destination_ata_at": null, + "destination_eta_at": "2024-06-27T06:42:00Z", + "pol_etd_at": null, + "pol_atd_at": "2024-06-05T08:03:00Z", + "pol_timezone": "Europe/Rome", + "pod_eta_at": null, + "pod_original_eta_at": null, + "pod_ata_at": "2024-06-23T15:34:00Z", + "pod_timezone": "America/New_York", + "line_tracking_last_attempted_at": "2024-06-26T16:28:39Z", + "line_tracking_last_succeeded_at": "2024-06-26T16:28:39Z", + "line_tracking_stopped_at": null, + "line_tracking_stopped_reason": null + }, + "links": { + "self": "/v2/shipments/7721a48c-5e93-43c9-9f5f-5be10a87fdde" + }, + "relationships": { + "port_of_lading": { + "data": { + "id": "b5656766-a56f-4b32-8e03-d240e7519604", + "type": "port" + } + }, + "port_of_discharge": { + "data": { + "id": "6129528d-846e-4571-ae16-b5328a4285ab", + "type": "port" + } + }, + "pod_terminal": { + "data": { + "id": "a243bdf8-0da3-4056-a6a7-05fe8ab43999", + "type": "terminal" + } + }, + "destination": { + "data": { + "id": "7daf9ea3-3018-4d62-b88c-43803df9030c", + "type": "port" + } + }, + "destination_terminal": { + "data": { + "id": "022ef8fc-1e2a-4ad6-8eae-330d65eb1c8e", + "type": "terminal" + } + }, + "line_tracking_stopped_by_user": { + "data": null + }, + "containers": { + "data": [ + { + "id": "2a25fd3e-a18e-47cc-9cea-62771e82d0f2", + "type": "container" + }, + { + "id": "6cdc725d-2b31-40f9-86dd-76225390a488", + "type": "container" + }, + { + "id": "2f1e9a9d-4689-4f4d-84a8-64409b56521d", + "type": "container" + } + ] + } + } + }, + { + "id": "32b5ad78-43ba-42d9-bdc0-4cf12320e020", + "type": "shipment", + "attributes": { + "created_at": "2024-06-26T15:59:52Z", + "ref_numbers": [], + "tags": [], + "bill_of_lading_number": "OOLU2738277190", + "normalized_number": "2738277190", + "shipping_line_scac": "OOLU", + "shipping_line_name": "Orient Overseas Container Line", + "shipping_line_short_name": "OOCL", + "customer_name": "Sodor Steamworks", + "port_of_lading_locode": "CNQDG", + "port_of_lading_name": "Qingdao", + "port_of_discharge_locode": "USLAX", + "port_of_discharge_name": "Los Angeles", + "pod_vessel_name": "EVER FORWARD", + "pod_vessel_imo": "9850551", + "pod_voyage_number": "1119E", + "destination_locode": "USMEM", + "destination_name": "Memphis", + "destination_timezone": "America/Chicago", + "destination_ata_at": null, + "destination_eta_at": "2024-07-01T14:00:00Z", + "pol_etd_at": null, + "pol_atd_at": "2024-06-02T18:22:00Z", + "pol_timezone": "Asia/Shanghai", + "pod_eta_at": null, + "pod_original_eta_at": null, + "pod_ata_at": "2024-06-22T13:36:00Z", + "pod_timezone": "America/Los_Angeles", + "line_tracking_last_attempted_at": "2024-06-26T15:59:52Z", + "line_tracking_last_succeeded_at": "2024-06-26T15:59:52Z", + "line_tracking_stopped_at": null, + "line_tracking_stopped_reason": null + }, + "links": { + "self": "/v2/shipments/32b5ad78-43ba-42d9-bdc0-4cf12320e020" + }, + "relationships": { + "port_of_lading": { + "data": { + "id": "0ccbe8af-c8d0-4abd-a842-3bfad1d82024", + "type": "port" + } + }, + "port_of_discharge": { + "data": { + "id": "47b27584-4ec9-4e2f-95e1-7a42928cc40c", + "type": "port" + } + }, + "pod_terminal": { + "data": { + "id": "42c53b13-0d29-4fa6-8663-7343a56319f1", + "type": "terminal" + } + }, + "destination": { + "data": { + "id": "ba0b9f68-2025-40ca-ae12-1c2210cca333", + "type": "port" + } + }, + "destination_terminal": { + "data": { + "id": "d0ec0da1-8a3a-4b11-b1e3-5716bbc71dc3", + "type": "terminal" + } + }, + "line_tracking_stopped_by_user": { + "data": null + }, + "containers": { + "data": [ + { + "id": "60d2fd62-14e2-4ca2-a927-9633fc58fdff", + "type": "container" + }, + { + "id": "0ffe3e75-b3df-4d20-b9f4-97bbbcec404d", + "type": "container" + } + ] + } + } + }, + { + "id": "bd117d3b-8fa4-487c-9bab-25c15e227d1a", + "type": "shipment", + "attributes": { + "created_at": "2024-06-26T15:59:35Z", + "ref_numbers": [], + "tags": [], + "bill_of_lading_number": "OOLU2147973020", + "normalized_number": "2147973020", + "shipping_line_scac": "OOLU", + "shipping_line_name": "Orient Overseas Container Line", + "shipping_line_short_name": "OOCL", + "customer_name": "Sodor Steamworks", + "port_of_lading_locode": "IDSUB", + "port_of_lading_name": "Surabaya", + "port_of_discharge_locode": "USLAX", + "port_of_discharge_name": "Los Angeles", + "pod_vessel_name": "CMA CGM A.LINCOLN", + "pod_vessel_imo": "9780859", + "pod_voyage_number": "1TU70E1MA", + "destination_locode": null, + "destination_name": null, + "destination_timezone": null, + "destination_ata_at": null, + "destination_eta_at": null, + "pol_etd_at": null, + "pol_atd_at": "2024-05-14T02:37:00Z", + "pol_timezone": "Asia/Jakarta", + "pod_eta_at": null, + "pod_original_eta_at": null, + "pod_ata_at": "2024-06-23T21:58:00Z", + "pod_timezone": "America/Los_Angeles", + "line_tracking_last_attempted_at": "2024-06-26T15:59:35Z", + "line_tracking_last_succeeded_at": "2024-06-26T15:59:35Z", + "line_tracking_stopped_at": null, + "line_tracking_stopped_reason": null + }, + "links": { + "self": "/v2/shipments/bd117d3b-8fa4-487c-9bab-25c15e227d1a" + }, + "relationships": { + "port_of_lading": { + "data": { + "id": "4201ab42-c51f-48ac-b7a1-12146e02c6a2", + "type": "port" + } + }, + "port_of_discharge": { + "data": { + "id": "47b27584-4ec9-4e2f-95e1-7a42928cc40c", + "type": "port" + } + }, + "pod_terminal": { + "data": { + "id": "eaa2580c-5f5b-4198-85e4-821145d62098", + "type": "terminal" + } + }, + "destination": { + "data": null + }, + "destination_terminal": { + "data": null + }, + "line_tracking_stopped_by_user": { + "data": null + }, + "containers": { + "data": [ + { + "id": "815ff702-fdb5-4455-a28e-314c345d7481", + "type": "container" + } + ] + } + } + } + ], + "meta": { + "size": 5, + "total": 34044 + }, + "links": { + "self": "https://api.terminal49.com/v2/shipments?page[size]=5", + "current": "https://api.terminal49.com/v2/shipments?page[number]=1&page[size]=5", + "next": "https://api.terminal49.com/v2/shipments?page[number]=2&page[size]=5", + "last": "https://api.terminal49.com/v2/shipments?page[number]=6809&page[size]=5" + } + } + } + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "errors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "examples": { + "Errors": { + "value": { + "error": [ + { + "title": "Invalid arrival_date_from", + "detail": "filter['arrival_from'] must be in the format 'YYYY-MM-DD' and a valid date" + }, + { + "title": "Invalid arrival_date_to", + "detail": "filter['arrival_from'] must be in the format 'YYYY-MM-DD' and a valid date" + }, + { + "title": "Invalid port_of_lading", + "detail": "port_of_discharge must be an array of 5 character UN/LOCODEs" + }, + { + "title": "Invalid port_of_discharge", + "detail": "port_of_discharge must be an array of 5 character UN/LOCODEs" + } + ] + } + } + } + } + } + } + }, + "operationId": "get-shipments", + "description": "Returns a list of your shipments. The shipments are returned sorted by creation date, with the most recent shipments appearing first.\n\nThis api will return all shipments associated with the account. Shipments created via the `tracking_request` API aswell as the ones added via the dashboard will be retuned via this endpoint. ", + "parameters": [ + { + "schema": { + "type": "integer", + "default": 1 + }, + "in": "query", + "name": "page[number]", + "description": "\n" + }, + { + "schema": { + "type": "integer", + "default": 30 + }, + "in": "query", + "name": "page[size]", + "description": "\n" + }, + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "q", + "description": "\nSearch shipments by master bill of lading, reference number, or container number.", + "deprecated": true + }, + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "include", + "description": "Comma delimited list of relations to include" + }, + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "number", + "description": "Search shipments by the original request tracking `request_number`" + } + ] + } + }, + "/shipments/{id}": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "id", + "in": "path", + "required": true, + "description": "Shipment Id" + } + ], + "get": { + "summary": "Get a shipment", + "tags": [ + "Shipments" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/shipment" + }, + "included": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/container" + }, + { + "$ref": "#/components/schemas/port" + }, + { + "$ref": "#/components/schemas/terminal" + } + ] + } + } + } + }, + "examples": { + "En-route to NY with inland move": { + "value": { + "data": { + "id": "02b1bd6f-407c-45bb-8645-06e7ee34e7e3", + "type": "shipment", + "attributes": { + "created_at": "2024-06-26T15:05:20Z", + "ref_numbers": [], + "tags": [], + "bill_of_lading_number": "MEDUF5399896", + "normalized_number": "MEDUF5399896", + "shipping_line_scac": "MSCU", + "shipping_line_name": "Mediterranean Shipping Company", + "shipping_line_short_name": "MSC", + "customer_name": "Sodor Steamworks", + "port_of_lading_locode": "FRLEH", + "port_of_lading_name": "Le Havre", + "port_of_discharge_locode": "USNYC", + "port_of_discharge_name": "New York / New Jersey", + "pod_vessel_name": "MSC ANISHA R.", + "pod_vessel_imo": "9227297", + "pod_voyage_number": "421A", + "destination_locode": "USIND", + "destination_name": "Indianapolis", + "destination_timezone": "US/Eastern", + "destination_ata_at": null, + "destination_eta_at": null, + "pol_etd_at": null, + "pol_atd_at": "2024-06-11T22:00:00Z", + "pol_timezone": "Europe/Paris", + "pod_eta_at": null, + "pod_original_eta_at": null, + "pod_ata_at": null, + "pod_timezone": "America/New_York", + "line_tracking_last_attempted_at": "2024-06-26T15:05:20Z", + "line_tracking_last_succeeded_at": "2024-06-26T15:05:20Z", + "line_tracking_stopped_at": null, + "line_tracking_stopped_reason": null + }, + "links": { + "self": "/v2/shipments/02b1bd6f-407c-45bb-8645-06e7ee34e7e3" + }, + "relationships": { + "port_of_lading": { + "data": { + "id": "fe7fc831-8a81-4079-8938-b90015912e8b", + "type": "port" + } + }, + "port_of_discharge": { + "data": { + "id": "f7cb530a-9e60-412c-a5bc-205a2f34ba54", + "type": "port" + } + }, + "pod_terminal": { + "data": { + "id": "b859f5c3-8515-41da-bf20-39c0a5ada887", + "type": "terminal" + } + }, + "destination": { + "data": { + "id": "e1e492f6-c8ba-45a9-ad1a-67a7e74547ce", + "type": "port" + } + }, + "destination_terminal": { + "data": null + }, + "line_tracking_stopped_by_user": { + "data": null + }, + "containers": { + "data": [ + { + "id": "55a700e4-7005-45a9-92fd-1ff38641dbd9", + "type": "container" + } + ] + } + } + }, + "links": { + "self": "https://api.terminal49.com/v2/shipments/02b1bd6f-407c-45bb-8645-06e7ee34e7e3?include=containers" + }, + "included": [ + { + "id": "55a700e4-7005-45a9-92fd-1ff38641dbd9", + "type": "container", + "attributes": { + "number": "CAIU7432986", + "seal_number": null, + "created_at": "2024-06-26T15:05:21Z", + "ref_numbers": [], + "pod_arrived_at": null, + "pod_discharged_at": "2024-06-22T04:00:00Z", + "final_destination_full_out_at": null, + "holds_at_pod_terminal": [], + "available_for_pickup": true, + "equipment_type": "dry", + "equipment_length": 40, + "equipment_height": "high_cube", + "weight_in_lbs": null, + "pod_full_out_at": null, + "empty_terminated_at": null, + "terminal_checked_at": "2024-06-26T17:51:12Z", + "fees_at_pod_terminal": [], + "pickup_lfd": "2024-07-07T04:00:00Z", + "pickup_appointment_at": null, + "pod_full_out_chassis_number": null, + "location_at_pod_terminal": "Yard - Y0709A", + "pod_last_tracking_request_at": "2024-06-26T17:51:12Z", + "shipment_last_tracking_request_at": "2024-06-26T15:05:20Z", + "availability_known": true, + "pod_timezone": "America/New_York", + "final_destination_timezone": "US/Eastern", + "empty_terminated_timezone": "US/Eastern", + "pod_rail_carrier_scac": "CSXT", + "ind_rail_carrier_scac": "CSXT", + "pod_rail_loaded_at": null, + "pod_rail_departed_at": null, + "ind_eta_at": null, + "ind_ata_at": null, + "ind_rail_unloaded_at": null, + "ind_facility_lfd_on": null + }, + "relationships": { + "shipment": { + "data": { + "id": "02b1bd6f-407c-45bb-8645-06e7ee34e7e3", + "type": "shipment" + } + }, + "pickup_facility": { + "data": null + }, + "pod_terminal": { + "data": { + "id": "b859f5c3-8515-41da-bf20-39c0a5ada887", + "type": "terminal" + } + }, + "transport_events": { + "data": [ + { + "id": "45b542cb-332b-4684-b915-42e3a0759823", + "type": "transport_event" + }, + { + "id": "174ed528-a1a9-4002-aef0-f2c9369199da", + "type": "transport_event" + }, + { + "id": "7a2f30a6-a756-4c14-9477-fbfc1c7fe2f8", + "type": "transport_event" + }, + { + "id": "e7365004-175a-46e8-96cd-dbed0f3daf21", + "type": "transport_event" + }, + { + "id": "7c567bf3-7f01-4a3d-a176-eaa1f7165585", + "type": "transport_event" + } + ] + }, + "raw_events": { + "data": [ + { + "id": "2956f71c-bfb9-4e49-b9e2-1b4d53c74cac", + "type": "raw_event" + }, + { + "id": "391e0eda-65b5-4fc3-a53d-25ecd9570259", + "type": "raw_event" + }, + { + "id": "74810c04-6c8a-4194-8cff-52936584a965", + "type": "raw_event" + }, + { + "id": "4b1500e2-b23b-4896-87bd-c38b1d16f385", + "type": "raw_event" + }, + { + "id": "8b9a7d88-720a-4304-8c1e-a3336e39f481", + "type": "raw_event" + }, + { + "id": "bf1f59c5-5dd8-4013-87f9-d7056bc87114", + "type": "raw_event" + } + ] + } + } + } + ] + } + } + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "errors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "examples": { + "En-route to NY with inland move": { + "value": { + "data": { + "id": "512ae32c-a604-44f8-9c15-2ca9cd3d2ae8", + "type": "shipment", + "attributes": { + "bill_of_lading_number": "6140575020", + "ref_numbers": [ + "my-ref-49", + "PO#18412" + ], + "created_at": "2020-02-20T13:37:12Z", + "tags": [], + "port_of_lading_locode": "INVTZ", + "port_of_lading_name": "Visakhapatnam, IN", + "port_of_discharge_locode": "USNYC", + "port_of_discharge_name": "New York / New Jersey, NY", + "destination_locode": "USDET", + "destination_name": "Detroit, MI", + "shipping_line_scac": "HLCU", + "pod_vessel_name": "CMA CGM ALMAVIVA", + "pod_voyage_number": "0108", + "pol_etd_at": null, + "pol_atd_at": "2020-02-15T21:53Z", + "pol_timezone": "Asia/Calcutta", + "pod_eta_at": "2020-03-18T08:00Z", + "pod_ata_at": null, + "pod_timezone": "America/New_York", + "destination_eta_at": "2020-03-26T17:00Z", + "destination_ata_at": null, + "destination_timezone": "America/Detroit" + }, + "relationships": { + "containers": { + "data": [ + { + "id": "c99a81c0-ff69-4bdf-aa5f-8e33a787f5fa", + "type": "container" + } + ] + }, + "port_of_lading": { + "data": { + "id": "bde5465a-1160-4fde-a026-74df9c362f65", + "type": "port" + } + }, + "port_of_discharge": { + "data": { + "id": "f7cb530a-9e60-412c-a5bc-205a2f34ba54", + "type": "port" + } + }, + "destination": { + "data": { + "id": "c9ae2b6b-5088-4e07-ba09-2872121e4fa2", + "type": "metro_area" + } + }, + "customer": { + "data": { + "id": "ff76b51a-371e-45ec-86d1-9d03ccae566a", + "type": "account" + } + } + } + }, + "included": [ + { + "id": "c99a81c0-ff69-4bdf-aa5f-8e33a787f5fa", + "type": "container", + "attributes": { + "number": "UACU4743531", + "equipment_type": "reefer", + "length": 40, + "height": "high_cube", + "weight_in_lbs": 35075, + "created_at": "2020-02-20T08:19:52Z", + "seal_number": null, + "pickup_lfd": null, + "availability_known": false, + "available_for_pickup": null, + "current_transportation_mode": "vessel", + "pod_discharged_at": null, + "pod_picked_up_at": null, + "destination_unloaded_at": null, + "destination_picked_up_at": null, + "empty_returned_at": null, + "pod_timezone": "America/Los_Angeles", + "final_destination_timezone": null, + "empty_terminated_timezone": null + }, + "relationships": { + "most_recent_location": { + "data": { + "id": "dd179094-a1d4-4129-842d-b952e43df4b7", + "type": "port" + } + }, + "shipment": { + "data": { + "id": "512ae32c-a604-44f8-9c15-2ca9cd3d2ae8", + "type": "shipment" + } + } + } + }, + { + "id": "bde5465a-1160-4fde-a026-74df9c362f65", + "type": "port", + "attributes": { + "name": "Visakhapatnam", + "code": "INVTZ", + "country_code": "IN", + "timezone": "Asia/Calcutta" + } + }, + { + "id": "dd179094-a1d4-4129-842d-b952e43df4b7", + "type": "port", + "attributes": { + "name": "Damietta", + "code": "EGDAM", + "country_code": "EG", + "time_zone": "Africa/Cairo" + } + }, + { + "id": "f7cb530a-9e60-412c-a5bc-205a2f34ba54", + "type": "port", + "attributes": { + "name": "New York / New Jersey", + "code": "USNYC", + "country_code": "US", + "timezone": "America/New_York" + } + }, + { + "id": "c9ae2b6b-5088-4e07-ba09-2872121e4fa2", + "type": "metro_area", + "attributes": { + "name": "Detroit", + "code": "USDET", + "country_code": "US", + "state_abbr": "MI", + "timezone": "America/Detroit" + } + }, + { + "id": "ff76b51a-371e-45ec-86d1-9d03ccae566a", + "type": "account", + "attributes": { + "name": "A-Z Imports" + } + }, + { + "id": "252a5450-2893-4207-b5c4-81ce3152ce84", + "type": "vessel", + "attributes": { + "name": "CMA CGM ALMAVIVA", + "imo": "9450648", + "mmsi": "228339600", + "latitude": 70.22625823437389, + "longitude": 45.06279126658865, + "nautical_speed_knots": 100, + "navigational_heading_degrees": 1, + "position_timestamp": "2023-06-05T19:46:18Z" + } + } + ] + } + } + } + } + } + } + }, + "operationId": "get-shipment-id", + "description": "Retrieves the details of an existing shipment. You need only supply the unique shipment `id` that was returned upon `tracking_request` creation.", + "parameters": [ + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "include", + "description": "Comma delimited list of relations to include" + } + ] + }, + "patch": { + "summary": "Edit a shipment", + "operationId": "patch-shipments-id", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/shipment" + } + } + } + } + } + } + }, + "description": "Update a shipment", + "tags": [ + "Shipments" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "required": [ + "attributes" + ], + "properties": { + "attributes": { + "type": "object", + "properties": { + "ref_numbers": { + "type": "array", + "example": [ + "REFNUMBER10" + ], + "description": "Shipment ref numbers.", + "items": { + "type": "string" + } + }, + "shipment_tags": { + "type": "array", + "x-stoplight": { + "id": "02itol38fmg55" + }, + "uniqueItems": true, + "description": "Tags related to a shipment", + "items": { + "x-stoplight": { + "id": "64x90wvr9d6nd" + }, + "type": "string", + "example": "tag1, tag2" + } + } + } + } + } + } + } + } + } + } + } + } + }, + "/shipments/{id}/stop_tracking": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "id", + "in": "path", + "required": true + } + ], + "patch": { + "summary": "Stop tracking a shipment", + "operationId": "patch-shipments-id-stop-tracking", + "tags": [ + "Shipments" + ], + "description": "We'll stop tracking the shipment, which means that there will be no more updates. You can still access the shipment's previously-collected information via the API or dashboard.\n\nYou can resume tracking a shipment by calling the `resume_tracking` endpoint, but keep in mind that some information is only made available by our data sources at specific times, so a stopped and resumed shipment may have some information missing.", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/shipment" + } + } + } + } + } + } + } + } + }, + "/shipments/{id}/resume_tracking": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "id", + "in": "path", + "required": true + } + ], + "patch": { + "summary": "Resume tracking a shipment", + "operationId": "patch-shipments-id-resume-tracking", + "tags": [ + "Shipments" + ], + "description": "Resume tracking a shipment. Keep in mind that some information is only made available by our data sources at specific times, so a stopped and resumed shipment may have some information missing.", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/shipment" + } + } + } + } + } + } + } + } + }, + "/tracking_requests": { + "post": { + "summary": "Create a tracking request", + "operationId": "post-track", + "responses": { + "201": { + "description": "Tracking Request Created", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/tracking_request" + }, + "included": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/account" + }, + { + "$ref": "#/components/schemas/shipping_line" + } + ] + } + } + } + }, + "examples": { + "Pending Tracking Request": { + "value": { + "data": { + "id": "ba4cb904-827f-4038-8e31-1e92b3356218", + "type": "tracking_request", + "attributes": { + "request_number": "MEDUFR030802", + "request_type": "bill_of_lading", + "scac": "MSCU", + "ref_numbers": [], + "created_at": "2020-04-04T16:13:35-07:00", + "updated_at": "2020-04-04T17:13:35-07:00", + "status": "pending", + "failed_reason": null + }, + "relationships": { + "tracked_object": { + "data": null + }, + "customer": { + "data": { + "id": "f7cb530a-9e60-412c-a5bc-205a2f34ba54", + "type": "party" + } + } + }, + "links": { + "self": "/v2/tracking_requests/ba4cb904-827f-4038-8e31-1e92b3356218" + } + } + } + }, + "Example: MSC BL": { + "value": { + "data": { + "attributes": { + "request_type": "bill_of_lading", + "request_number": "MEDUFR030802", + "ref_numbers": [ + "PO12345", + "HBL12345", + "CUSREF1234" + ], + "shipment_tags": [ + "camembert" + ], + "scac": "MSCU" + }, + "relationships": { + "customer": { + "data": { + "id": "f7cb530a-9e60-412c-a5bc-205a2f34ba54", + "type": "party" + } + } + }, + "type": "tracking_request" + } + }, + "summary": "Track by Bill of Lading" + }, + "Example: Booking Number": { + "value": { + "data": { + "attributes": { + "request_type": "booking_number", + "request_number": "OOLU8324567", + "ref_numbers": [ + "PO-8765", + "INV-12345" + ], + "shipment_tags": [ + "priority", + "electronics" + ], + "scac": "OOLU" + }, + "relationships": { + "customer": { + "data": { + "id": "a9e52f3d-2fa9-467c-8deb-09e90dac2f0b", + "type": "party" + } + } + }, + "type": "tracking_request" + } + }, + "summary": "Track by Booking Number with References, Tags and Customer" + }, + "Example: Container": { + "value": { + "data": { + "type": "tracking_request", + "attributes": { + "request_type": "container", + "request_number": "MSCU1234567", + "scac": "MSCU" + } + } + }, + "summary": "Track by Container Number" + }, + "Example: Test Number": { + "value": { + "data": { + "type": "tracking_request", + "attributes": { + "request_type": "bill_of_lading", + "request_number": "TEST-TR-SUCCEEDED", + "scac": "TEST" + } + } + }, + "summary": "Using Test Numbers for Development" + } + } + } + }, + "headers": {} + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "errors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "examples": { + "Error Examples": { + "value": { + "errors": [ + { + "status": "422", + "source": { + "pointer": "/data/attributes/scac" + }, + "title": "Unprocessable Entity", + "detail": "Scac can't be blank", + "code": "blank" + }, + { + "status": "422", + "source": { + "pointer": "/data/attributes/scac" + }, + "title": "Unprocessable Entity", + "detail": "Scac 'XXXX' is not recognized", + "code": "blank" + }, + { + "status": "422", + "source": { + "pointer": "/data/attributes/scac" + }, + "title": "Unprocessable Entity", + "detail": "Scac 'UALC' is not supported. We do not currently integrate with Universal Africa Lines", + "code": "blank" + }, + { + "status": "422", + "source": { + "pointer": "/data/attributes/request_number" + }, + "title": "Unprocessable Entity", + "detail": "Request number can't be blank", + "code": "blank" + } + ] + } + } + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "attributes": { + "type": "object", + "properties": { + "request_type": { + "type": "string", + "example": "bill_of_lading", + "enum": [ + "bill_of_lading", + "booking_number", + "container" + ], + "description": " The type of document number to be supplied. Container number support is currently in BETA." + }, + "request_number": { + "type": "string", + "example": "MEDUFR030802" + }, + "scac": { + "type": "string", + "example": "MSCU", + "minLength": 4, + "maxLength": 4 + }, + "ref_numbers": { + "type": "array", + "description": "Optional list of reference numbers to be added to the shipment when tracking request completes", + "items": { + "type": "string" + } + }, + "shipment_tags": { + "type": "array", + "description": "Optional list of tags to be added to the shipment when tracking request completes", + "items": { + "type": "string" + } + } + }, + "required": [ + "request_type", + "request_number", + "scac" + ] + }, + "relationships": { + "type": "object", + "properties": { + "customer": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "party" + ] + } + } + } + } + } + } + }, + "type": { + "enum": [ + "tracking_request" + ], + "type": "string" + } + }, + "required": [ + "type" + ] + } + } + }, + "examples": { + "Example: MSC BL": { + "value": { + "data": { + "attributes": { + "request_type": "bill_of_lading", + "request_number": "MEDUFR030802", + "ref_numbers": [ + "PO12345", + "HBL12345", + "CUSREF1234" + ], + "shipment_tags": [ + "camembert" + ], + "scac": "MSCU" + }, + "relationships": { + "customer": { + "data": { + "id": "f7cb530a-9e60-412c-a5bc-205a2f34ba54", + "type": "party" + } + } + }, + "type": "tracking_request" + } + } + }, + "Example: Booking Number": { + "value": { + "data": { + "attributes": { + "request_type": "booking_number", + "request_number": "OOLU8324567", + "ref_numbers": [ + "PO-8765", + "INV-12345" + ], + "shipment_tags": [ + "priority", + "electronics" + ], + "scac": "OOLU" + }, + "relationships": { + "customer": { + "data": { + "id": "a9e52f3d-2fa9-467c-8deb-09e90dac2f0b", + "type": "party" + } + } + }, + "type": "tracking_request" + } + } + }, + "Example: Container": { + "value": { + "data": { + "type": "tracking_request", + "attributes": { + "request_type": "container", + "request_number": "MSCU1234567", + "scac": "MSCU" + } + } + } + }, + "Example: Test Number": { + "value": { + "data": { + "type": "tracking_request", + "attributes": { + "request_type": "bill_of_lading", + "request_number": "TEST-TR-SUCCEEDED", + "scac": "TEST" + } + } + } + } + } + } + }, + "description": "Create a shipment tracking request" + }, + "security": [ + { + "authorization": [] + } + ], + "description": "To track an ocean shipment, you create a new tracking request. \nTwo attributes are required to track a shipment. A `bill of lading/booking number` and a shipping line `SCAC`. \n\nOnce a tracking request is created we will attempt to fetch the shipment details and it's related containers from the shipping line. If the attempt is successful we will create in new shipment object including any related container objects. We will send a `tracking_request.succeeded` webhook notification to your webhooks. \n\nIf the attempt to fetch fails then we will send a `tracking_request.failed` webhook notification to your `webhooks`. \n\nA `tracking_request.succeeded` or `tracking_request.failed` webhook notificaiton will only be sent if you have atleast one active webhook. ", + "tags": [ + "Tracking Requests" + ], + "parameters": [] + }, + "parameters": [], + "get": { + "summary": "List tracking requests", + "operationId": "get-tracking-requests", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/tracking_request" + } + }, + "links": { + "$ref": "#/components/schemas/links" + }, + "meta": { + "$ref": "#/components/schemas/meta" + }, + "included": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/account" + }, + { + "$ref": "#/components/schemas/shipping_line" + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "shipment" + ] + }, + "links": { + "type": "object", + "properties": { + "self": { + "type": "string", + "format": "uri" + } + } + } + }, + "description": "" + } + ] + } + } + } + }, + "examples": {} + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "errors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/error" + } + } + } + } + } + } + } + }, + "description": "Returns a list of your tracking requests. The tracking requests are returned sorted by creation date, with the most recent tracking request appearing first.", + "tags": [ + "Tracking Requests" + ], + "parameters": [ + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "q", + "description": "A search term to be applied against request_number and reference_numbers.", + "deprecated": true + }, + { + "schema": { + "type": "string", + "enum": [ + "created", + "pending", + "failed" + ], + "example": "created" + }, + "in": "query", + "name": "filter[status]", + "description": "filter by `status`" + }, + { + "schema": { + "type": "string", + "example": "MSCU" + }, + "in": "query", + "name": "filter[scac]", + "description": "filter by shipping line `scac`" + }, + { + "schema": { + "type": "string", + "example": "2020-04-28T22:59:15Z", + "format": "date-time" + }, + "in": "query", + "name": "filter[created_at][start]", + "description": "filter by tracking_requests `created_at` after a certain ISO8601 timestamp" + }, + { + "schema": { + "type": "string", + "format": "date-time", + "example": "2020-04-28T22:59:15Z" + }, + "in": "query", + "description": "filter by tracking_requests `created_at` before a certain ISO8601 timestamp", + "name": "filter[created_at][end]" + }, + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "include", + "description": "Comma delimited list of relations to include. 'tracked_object' is included by default." + }, + { + "schema": { + "type": "integer" + }, + "in": "query", + "name": "page[number]" + }, + { + "schema": { + "type": "integer" + }, + "in": "query", + "name": "page[size]" + }, + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "filter[request_number]", + "description": "filter by `request_number`" + } + ] + } + }, + "/tracking_requests/{id}": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "id", + "in": "path", + "required": true, + "description": "Tracking Request ID" + } + ], + "get": { + "summary": "Get a single tracking request", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/tracking_request" + }, + "included": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/account" + }, + { + "$ref": "#/components/schemas/shipment" + }, + { + "$ref": "#/components/schemas/shipping_line" + } + ] + } + } + } + }, + "examples": { + "With Status Created": { + "value": { + "data": { + "id": "ba4cb904-827f-4038-8e31-1e92b3356218", + "type": "tracking_request", + "attributes": { + "request_number": "MEDUFR030802", + "request_type": "bill_of_lading", + "scac": "MSCU", + "ref_numbers": [], + "created_at": "2020-04-04T16:13:35-07:00", + "updated_at": "2020-04-04T17:13:35-07:00", + "status": "created", + "failed_reason": null + }, + "relationships": { + "tracked_object": { + "data": { + "id": "eb6f218a-0b4a-47f9-8ef9-759aa5e0ea83", + "type": "shipment" + } + } + }, + "links": { + "self": "/v2/tracking_requests/ba4cb904-827f-4038-8e31-1e92b3356218" + } + }, + "included": [ + { + "id": "eb6f218a-0b4a-47f9-8ef9-759aa5e0ea83", + "type": "shipment", + "attributes": { + "created_at": "2020-04-04T16:13:37-07:00", + "bill_of_lading_number": "MEDUFR030802", + "ref_numbers": [], + "shipping_line_scac": "MSCU", + "shipping_line_name": "Mediterranean Shipping Company", + "port_of_lading_locode": "FRFOS", + "port_of_lading_name": "Fos-Sur-Mer", + "port_of_discharge_locode": "USOAK", + "port_of_discharge_name": "Oakland", + "pod_vessel_name": "MSC ALGECIRAS", + "pod_vessel_imo": "9605243", + "pod_voyage_number": "920A", + "destination_locode": "USOAK", + "destination_name": "Oakland", + "destination_timezone": "America/Los_Angeles", + "destination_ata_at": "2019-06-21T18:46:00-07:00", + "destination_eta_at": null, + "pol_etd_at": null, + "pol_atd_at": "2019-05-24T03:00:00-07:00", + "pol_timezone": "Europe/Paris", + "pod_eta_at": null, + "pod_ata_at": "2019-06-21T18:46:00-07:00", + "pod_timezone": "America/Los_Angeles" + }, + "relationships": { + "port_of_lading": { + "data": { + "id": "6d8c6c29-72a6-49ad-87b7-fd045f202212", + "type": "port" + } + }, + "port_of_discharge": { + "data": { + "id": "42d1ba3a-f4b8-431d-a6fe-49fd748a59e7", + "type": "port" + } + }, + "pod_terminal": { + "data": null + }, + "destination": { + "data": { + "id": "42d1ba3a-f4b8-431d-a6fe-49fd748a59e7", + "type": "port" + } + }, + "containers": { + "data": [ + { + "id": "11c1fa10-52a5-48e2-82f4-5523756b3d0f", + "type": "container" + } + ] + } + }, + "links": { + "self": "/v2/shipments/eb6f218a-0b4a-47f9-8ef9-759aa5e0ea83" + } + } + ] + } + }, + "Multiple containers": { + "value": { + "data": { + "id": "62c30bd4-d7fc-40dc-9fd6-fb39224301f5", + "type": "tracking_request", + "attributes": { + "request_number": "212157148", + "request_type": "bill_of_lading", + "scac": "MAEU", + "ref_numbers": [], + "shipment_tags": [], + "created_at": "2021-07-27T16:44:14Z", + "updated_at": "2021-07-27T17:44:14Z", + "status": "created", + "failed_reason": null, + "is_retrying": false, + "retry_count": null + }, + "relationships": { + "tracked_object": { + "data": { + "id": "dfc9f601-f6fe-412e-a71c-feabcc2dd4e3", + "type": "shipment" + } + }, + "customer": { + "data": null + } + }, + "links": { + "self": "/v2/tracking_requests/62c30bd4-d7fc-40dc-9fd6-fb39224301f5" + } + }, + "links": { + "self": "https://api.terminal49.com/v2/tracking_requests/62c30bd4-d7fc-40dc-9fd6-fb39224301f5?filter%5Bstatus%5D=created" + }, + "included": [ + { + "id": "dfc9f601-f6fe-412e-a71c-feabcc2dd4e3", + "type": "shipment", + "attributes": { + "created_at": "2021-07-27T16:44:16Z", + "ref_numbers": null, + "tags": [], + "bill_of_lading_number": "212157148", + "shipping_line_scac": "MAEU", + "shipping_line_name": "Maersk", + "shipping_line_short_name": "Maersk", + "port_of_lading_locode": "MYTPP", + "port_of_lading_name": "Tanjung Pelepas", + "port_of_discharge_locode": null, + "port_of_discharge_name": null, + "pod_vessel_name": null, + "pod_vessel_imo": null, + "pod_voyage_number": null, + "destination_locode": null, + "destination_name": null, + "destination_timezone": null, + "destination_ata_at": null, + "destination_eta_at": null, + "pol_etd_at": null, + "pol_atd_at": null, + "pol_timezone": "Asia/Kuala_Lumpur", + "pod_eta_at": "2021-09-15T15:00:00Z", + "pod_ata_at": null, + "pod_timezone": null, + "line_tracking_last_attempted_at": null, + "line_tracking_last_succeeded_at": "2021-07-27T16:44:16Z", + "line_tracking_stopped_at": null, + "line_tracking_stopped_reason": null + }, + "relationships": { + "port_of_lading": { + "data": { + "id": "6c387786-252c-476d-9f99-7d835b6b978b", + "type": "port" + } + }, + "port_of_discharge": { + "data": null + }, + "pod_terminal": { + "data": null + }, + "destination": { + "data": null + }, + "destination_terminal": { + "data": null + }, + "containers": { + "data": [ + { + "id": "965880c9-a37e-4ed7-a060-9c49c0f0c5ed", + "type": "container" + }, + { + "id": "ea1f8e08-fcdf-498d-9cb5-0c370b023eeb", + "type": "container" + }, + { + "id": "67f55105-8ea2-4137-9244-f9cc204f5766", + "type": "container" + }, + { + "id": "5ab5d058-772c-466c-bc73-0b8767ad5a79", + "type": "container" + } + ] + } + }, + "links": { + "self": "/v2/shipments/dfc9f601-f6fe-412e-a71c-feabcc2dd4e3" + } + } + ] + } + } + } + } + } + }, + "404": { + "description": "Not Found", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "errors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "examples": {} + } + } + } + }, + "operationId": "get-track-request-by-id", + "description": "Get the details and status of an existing tracking request. ", + "tags": [ + "Tracking Requests" + ], + "parameters": [ + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "include", + "description": "Comma delimited list of relations to include. 'tracked_object' is included by default." + } + ], + "x-internal": false + }, + "patch": { + "summary": "Edit a tracking request", + "operationId": "patch-track-request-by-id", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/tracking_request" + } + } + } + } + } + } + }, + "description": "Update a tracking request", + "tags": [ + "Tracking Requests" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "type": "object", + "properties": { + "ref_number": { + "type": "string", + "example": "REFNUMBER11", + "description": "Tracking request ref number." + } + } + } + } + } + } + } + } + } + } + } + }, + "/webhooks/{id}": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "id", + "in": "path", + "required": true + } + ], + "get": { + "summary": "Get single webhook", + "tags": [ + "Webhooks" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/webhook" + } + } + } + } + } + } + }, + "operationId": "get-webhooks-id", + "description": "Get the details of a single webhook" + }, + "patch": { + "summary": "Edit a webhook", + "operationId": "patch-webhooks-id", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/webhook" + } + } + } + } + } + } + }, + "description": "Update a single webhook", + "tags": [ + "Webhooks" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "type": "object", + "properties": { + "url": { + "type": "string", + "example": "https://webhook.site/#!/39084fbb-d887-42e8-be08-b9183ad02362", + "format": "uri", + "description": "The URL of the webhook endpoint." + }, + "events": { + "type": "array", + "description": "The list of events to enable for this endpoint.", + "uniqueItems": true, + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "container.transport.vessel_arrived", + "container.transport.vessel_discharged", + "container.transport.vessel_loaded", + "container.transport.vessel_departed", + "container.transport.rail_departed", + "container.transport.rail_arrived", + "container.transport.rail_loaded", + "container.transport.rail_unloaded", + "container.transport.transshipment_arrived", + "container.transport.transshipment_discharged", + "container.transport.transshipment_loaded", + "container.transport.transshipment_departed", + "container.transport.feeder_arrived", + "container.transport.feeder_discharged", + "container.transport.feeder_loaded", + "container.transport.feeder_departed", + "container.transport.empty_out", + "container.transport.full_in", + "container.transport.full_out", + "container.transport.empty_in", + "container.transport.vessel_berthed", + "shipment.estimated.arrival", + "tracking_request.succeeded", + "tracking_request.failed", + "tracking_request.awaiting_manifest", + "tracking_request.tracking_stopped", + "container.created", + "container.updated", + "container.pod_terminal_changed", + "container.transport.arrived_at_inland_destination", + "container.transport.estimated.arrived_at_inland_destination", + "container.pickup_lfd.changed" + ] + } + }, + "active": { + "type": "boolean" + }, + "headers": { + "type": "array", + "description": "Optional custom headers to pass with each webhook invocation", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of the header. (Please not this will be auto-capitalized) " + }, + "value": { + "type": "string", + "description": "The value to pass for the header\n" + } + } + } + } + } + }, + "type": { + "type": "string", + "enum": [ + "webhook" + ] + } + } + } + }, + "required": [ + "data" + ] + }, + "examples": {} + } + } + } + }, + "delete": { + "summary": "Delete a webhook", + "operationId": "delete-webhooks-id", + "responses": { + "200": { + "description": "OK" + } + }, + "description": "Delete a webhook", + "tags": [ + "Webhooks" + ] + } + }, + "/webhooks": { + "get": { + "summary": "List webhooks", + "tags": [ + "Webhooks" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/webhook" + } + }, + "meta": { + "$ref": "#/components/schemas/meta" + }, + "links": { + "$ref": "#/components/schemas/links" + } + } + }, + "examples": { + "example-1": { + "value": { + "data": [ + { + "id": "497f6eca-6276-4993-bfeb-53cbbbba6f08", + "type": "webhook", + "attributes": { + "url": "http://example.com", + "active": true, + "events": [ + "tracking_request.succeeded" + ], + "secret": "672bd7b58b54645934a830d8fa", + "headers": [ + { + "name": "x-secret-sauce", + "value": "sriracha" + } + ] + } + } + ], + "meta": { + "size": 0, + "total": 0 + }, + "links": { + "last": "http://example.com", + "next": "http://example.com", + "prev": "http://example.com", + "first": "http://example.com", + "self": "http://example.com" + } + } + } + } + } + } + } + }, + "operationId": "get-webhooks", + "description": "Get a list of all the webhooks", + "parameters": [ + { + "schema": { + "type": "integer" + }, + "in": "query", + "name": "page[number]" + }, + { + "schema": { + "type": "integer" + }, + "in": "query", + "name": "page[size]" + } + ] + }, + "post": { + "summary": "Create a webhook", + "operationId": "post-webhooks", + "tags": [ + "Webhooks" + ], + "description": "You can configure a webhook via the API to be notified about events that happen in your Terminal49 account. These events can be realted to tracking_requests, shipments and containers. \n\nThis is the recommended way tracking shipments and containers via the API. You should use this instead of polling our the API periodically. ", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "type": "object", + "required": [ + "url", + "active" + ], + "properties": { + "url": { + "type": "string", + "example": "https://webhook.site/#!/39084fbb-d887-42e8-be08-b9183ad02362", + "format": "uri", + "description": "The URL of the webhook endpoint." + }, + "events": { + "type": "array", + "uniqueItems": true, + "description": "The list of events to enable for this endpoint.", + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "container.transport.vessel_arrived", + "container.transport.vessel_discharged", + "container.transport.vessel_loaded", + "container.transport.vessel_departed", + "container.transport.rail_departed", + "container.transport.rail_arrived", + "container.transport.rail_loaded", + "container.transport.rail_unloaded", + "container.transport.transshipment_arrived", + "container.transport.transshipment_discharged", + "container.transport.transshipment_loaded", + "container.transport.transshipment_departed", + "container.transport.feeder_arrived", + "container.transport.feeder_discharged", + "container.transport.feeder_loaded", + "container.transport.feeder_departed", + "container.transport.empty_out", + "container.transport.full_in", + "container.transport.full_out", + "container.transport.empty_in", + "container.transport.vessel_berthed", + "shipment.estimated.arrival", + "tracking_request.succeeded", + "tracking_request.failed", + "tracking_request.awaiting_manifest", + "tracking_request.tracking_stopped", + "container.created", + "container.updated", + "container.pod_terminal_changed", + "container.transport.arrived_at_inland_destination", + "container.transport.estimated.arrived_at_inland_destination", + "container.pickup_lfd.changed" + ], + "example": "tracking_request.succeeded" + } + }, + "active": { + "type": "boolean" + }, + "headers": { + "type": "array", + "description": "Optional custom headers to pass with each webhook invocation", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of the header. (Please note this will be auto-capitalized) " + }, + "value": { + "type": "string", + "description": "The value to pass for the header\n" + } + } + } + } + } + }, + "type": { + "type": "string", + "enum": [ + "webhook" + ] + } + } + } + }, + "required": [ + "data" + ] + }, + "examples": { + "Test Webhook (all events)": { + "value": { + "data": { + "attributes": { + "url": "https://webhook.site/", + "events": [ + "container.transport.vessel_arrived", + "container.transport.vessel_discharged", + "container.transport.vessel_loaded", + "container.transport.vessel_departed", + "container.transport.rail_departed", + "container.transport.rail_arrived", + "container.transport.rail_loaded", + "container.transport.rail_unloaded", + "container.transport.transshipment_arrived", + "container.transport.transshipment_discharged", + "container.transport.transshipment_loaded", + "container.transport.transshipment_departed", + "container.transport.feeder_arrived", + "container.transport.feeder_discharged", + "container.transport.feeder_loaded", + "container.transport.feeder_departed", + "container.transport.empty_out", + "container.transport.full_in", + "container.transport.full_out", + "container.transport.empty_in", + "container.transport.vessel_berthed", + "shipment.estimated.arrival", + "tracking_request.succeeded", + "tracking_request.failed", + "tracking_request.awaiting_manifest", + "tracking_request.tracking_stopped", + "container.created", + "container.updated", + "container.pod_terminal_changed", + "container.transport.arrived_at_inland_destination", + "container.transport.estimated.arrived_at_inland_destination", + "container.pickup_lfd.changed" + ], + "active": true + }, + "type": "webhook" + } + } + } + } + } + }, + "description": "" + }, + "responses": { + "201": { + "description": "Create a test webhook endpoint", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/webhook" + } + } + }, + "examples": { + "Test Endpoint Created": { + "value": { + "data": { + "type": "webhook", + "id": "9809fb96-7754-488f-99df-29ca8d410d89", + "attributes": { + "url": "https://webhook.site/", + "active": true, + "events": [ + "tracking_request.succeeded" + ], + "secret": "C193J3QOXMFH", + "created_at": "2020-06-05T19:06:13Z" + } + } + } + } + } + }, + "application/xml": { + "schema": { + "$ref": "#/components/schemas/webhook" + } + } + } + } + } + }, + "parameters": [] + }, + "/webhook_notifications/{id}": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "id", + "in": "path", + "required": true + } + ], + "get": { + "summary": "Get a single webhook notification", + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/webhook_notification" + }, + "included": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/webhook" + }, + { + "$ref": "#/components/schemas/tracking_request" + }, + { + "$ref": "#/components/schemas/transport_event" + }, + { + "$ref": "#/components/schemas/estimated_event" + }, + { + "$ref": "#/components/schemas/container_updated_event" + } + ] + } + } + } + }, + "examples": { + "Tracking Request": { + "value": { + "data": { + "id": "a76187fc-5749-43f9-9053-cfaad9790a31", + "type": "webhook_notification", + "attributes": { + "id": "a76187fc-5749-43f9-9053-cfaad9790a31", + "event": "tracking_request.succeeded", + "delivery_status": "pending", + "created_at": "2020-09-11T21:25:34Z" + }, + "relationships": { + "reference_object": { + "data": { + "id": "bdeca506-9741-4ab1-a0a7-cfd1d908e923", + "type": "tracking_request" + } + }, + "webhook": { + "data": { + "id": "914b21ce-dd7d-4c49-8503-65aba488e9a9", + "type": "webhook" + } + }, + "webhook_notification_logs": { + "data": [] + } + } + }, + "included": [ + { + "id": "bdeca506-9741-4ab1-a0a7-cfd1d908e923", + "type": "tracking_request", + "attributes": { + "request_number": "TE497ED1063E", + "request_type": "bill_of_lading", + "scac": "MSCU", + "ref_numbers": [], + "created_at": "2020-09-11T21:25:34Z", + "updated_at": "2020-09-11T22:25:34Z", + "status": "created", + "failed_reason": null, + "is_retrying": false, + "retry_count": null + }, + "relationships": { + "tracked_object": { + "data": { + "id": "b5b10c0a-8d18-46da-b4c2-4e5fa790e7da", + "type": "shipment" + } + } + }, + "links": { + "self": "/v2/tracking_requests/bdeca506-9741-4ab1-a0a7-cfd1d908e923" + } + }, + { + "id": "b5b10c0a-8d18-46da-b4c2-4e5fa790e7da", + "type": "shipment", + "attributes": { + "created_at": "2020-09-11T21:25:33Z", + "bill_of_lading_number": "TE497ED1063E", + "ref_numbers": [], + "shipping_line_scac": "MSCU", + "shipping_line_name": "Mediterranean Shipping Company", + "port_of_lading_locode": "MXZLO", + "port_of_lading_name": "Manzanillo", + "port_of_discharge_locode": "USOAK", + "port_of_discharge_name": "Port of Oakland", + "pod_vessel_name": "MSC CHANNE", + "pod_vessel_imo": "9710438", + "pod_voyage_number": "098N", + "destination_locode": null, + "destination_name": null, + "destination_timezone": null, + "destination_ata_at": null, + "destination_eta_at": null, + "pol_etd_at": null, + "pol_atd_at": "2020-08-29T21:25:33Z", + "pol_timezone": "America/Mexico_City", + "pod_eta_at": "2020-09-18T21:25:33Z", + "pod_ata_at": null, + "pod_timezone": "America/Los_Angeles" + }, + "relationships": { + "port_of_lading": { + "data": { + "id": "4384d6a5-5ccc-43b7-8d19-4a9525e74c08", + "type": "port" + } + }, + "port_of_discharge": { + "data": { + "id": "2a765fdd-c479-4345-b71d-c4ef839952e2", + "type": "port" + } + }, + "pod_terminal": { + "data": { + "id": "17891bc8-52da-40bf-8ff0-0247ec05faf1", + "type": "terminal" + } + }, + "destination": { + "data": null + }, + "containers": { + "data": [ + { + "id": "b2fc728c-e2f5-4a99-8899-eb7b34ef22d7", + "type": "container" + } + ] + } + }, + "links": { + "self": "/v2/shipments/b5b10c0a-8d18-46da-b4c2-4e5fa790e7da" + } + }, + { + "id": "b2fc728c-e2f5-4a99-8899-eb7b34ef22d7", + "type": "container", + "attributes": { + "number": "ARDU1824900", + "seal_number": "139F1451", + "created_at": "2020-09-11T21:25:34Z", + "equipment_type": "dry", + "equipment_length": 40, + "equipment_height": "standard", + "weight_in_lbs": 53507, + "fees_at_pod_terminal": [], + "holds_at_pod_terminal": [], + "pickup_lfd": null, + "pickup_appointment_at": null, + "availability_known": true, + "available_for_pickup": false, + "pod_arrived_at": null, + "pod_discharged_at": null, + "final_destination_full_out_at": null, + "pod_full_out_at": null, + "empty_terminated_at": null, + "pod_timezone": "America/Los_Angeles", + "final_destination_timezone": null, + "empty_terminated_timezone": null + }, + "relationships": { + "shipment": { + "data": { + "id": "b5b10c0a-8d18-46da-b4c2-4e5fa790e7da", + "type": "shipment" + } + }, + "pod_terminal": { + "data": { + "id": "17891bc8-52da-40bf-8ff0-0247ec05faf1", + "type": "terminal" + } + }, + "transport_events": { + "data": [ + { + "id": "56078596-5293-4c84-9245-cca00a787265", + "type": "transport_event" + } + ] + } + } + }, + { + "id": "56078596-5293-4c84-9245-cca00a787265", + "type": "transport_event", + "attributes": { + "event": "container.transport.vessel_departed", + "created_at": "2020-09-11T21:25:34Z", + "voyage_number": null, + "timestamp": "2020-08-29T21:25:33Z", + "location_locode": "MXZLO", + "timezone": "America/Los_Angeles" + }, + "relationships": { + "shipment": { + "data": { + "id": "b5b10c0a-8d18-46da-b4c2-4e5fa790e7da", + "type": "shipment" + } + }, + "container": { + "data": { + "id": "b2fc728c-e2f5-4a99-8899-eb7b34ef22d7", + "type": "container" + } + }, + "vessel": { + "data": null + }, + "location": { + "data": { + "id": "2a765fdd-c479-4345-b71d-c4ef839952e2", + "type": "port" + } + }, + "terminal": { + "data": null + } + } + } + ] + } + }, + "Estimated Event": { + "value": { + "data": { + "id": "d7e04138-b59d-4c41-9d2d-251d95bedd6e", + "type": "webhook_notification", + "attributes": { + "id": "d7e04138-b59d-4c41-9d2d-251d95bedd6e", + "event": "shipment.estimated.arrival", + "delivery_status": "pending", + "created_at": "2020-09-11T21:25:34Z" + }, + "relationships": { + "reference_object": { + "data": { + "id": "b68bc6cb-2c37-43f6-889b-86a16b2b6fe6", + "type": "estimated_event" + } + }, + "webhook": { + "data": { + "id": "614eab61-ae3c-4d40-bbe9-41200a172691", + "type": "webhook" + } + } + } + }, + "included": [ + { + "id": "b68bc6cb-2c37-43f6-889b-86a16b2b6fe6", + "type": "estimated_event", + "attributes": { + "created_at": "2020-04-06T19:02:46-07:00", + "estimated_timestamp": "2020-04-09T19:02:46-07:00", + "voyage_number": "A1C", + "event": "shipment.estimated.arrival", + "timezone": "America/Los_Angeles" + }, + "relationships": { + "shipment": { + "data": { + "id": "715ed64b-6195-49f6-9407-1383a8088bfd", + "type": "shipment" + } + }, + "port": { + "data": { + "id": "ed4001a5-ad9d-43c3-883c-79354f422510", + "type": "port" + } + }, + "vessel": { + "data": { + "id": "ebf68c6c-9d0d-4383-aa41-e097009dfb4c", + "type": "vessel" + } + } + } + }, + { + "id": "ed4001a5-ad9d-43c3-883c-79354f422510", + "type": "port", + "attributes": { + "id": "ed4001a5-ad9d-43c3-883c-79354f422510", + "name": "Port of Oakland", + "code": "USOAK", + "state_abbr": "CA", + "city": "Oakland", + "country_code": "US", + "time_zone": "America/Los_Angeles" + } + }, + { + "id": "715ed64b-6195-49f6-9407-1383a8088bfd", + "type": "shipment", + "attributes": { + "created_at": "2020-04-06T19:02:46-07:00", + "bill_of_lading_number": "TE49DD6650B9", + "ref_numbers": [ + "REF-4A25EA" + ], + "shipping_line_scac": "MSCU", + "shipping_line_name": "Mediterranean Shipping Company", + "port_of_lading_locode": "MXZLO", + "port_of_lading_name": "Manzanillo", + "port_of_discharge_locode": "USOAK", + "port_of_discharge_name": "Port of Oakland", + "pod_vessel_name": "MSC CHANNE", + "pod_vessel_imo": "9710438", + "pod_voyage_number": "098N", + "destination_locode": null, + "destination_name": null, + "destination_timezone": null, + "destination_ata_at": null, + "destination_eta_at": null, + "pol_etd_at": null, + "pol_atd_at": null, + "pol_timezone": "America/Mexico_City", + "pod_eta_at": "2020-04-13T19:02:46-07:00", + "pod_ata_at": null, + "pod_timezone": "America/Los_Angeles" + }, + "relationships": { + "port_of_lading": { + "data": { + "id": "1378c720-efe9-4562-a2ad-562002eb4b1d", + "type": "port" + } + }, + "port_of_discharge": { + "data": { + "id": "ed4001a5-ad9d-43c3-883c-79354f422510", + "type": "port" + } + }, + "pod_terminal": { + "data": { + "id": "2508d879-4451-4d7f-ab23-92258b5df553", + "type": "terminal" + } + }, + "destination": { + "data": null + }, + "containers": { + "data": [] + } + }, + "links": { + "self": "/v2/shipments/715ed64b-6195-49f6-9407-1383a8088bfd" + } + } + ] + } + }, + "Transport Event": { + "value": { + "data": { + "id": "abec839a-48fe-4540-93d7-d3ea3d67bdbf", + "type": "webhook_notification", + "attributes": { + "id": "abec839a-48fe-4540-93d7-d3ea3d67bdbf", + "event": "container.transport.vessel_arrived", + "delivery_status": "pending", + "created_at": "2020-07-28T23:12:53Z" + }, + "relationships": { + "reference_object": { + "data": { + "id": "a6ecb8ab-98d6-4cab-8487-ce9dd7be082b", + "type": "transport_event" + } + }, + "webhook": { + "data": { + "id": "534d498b-8332-439a-accb-129dfd144ceb", + "type": "webhook" + } + }, + "webhook_notification_logs": { + "data": [] + } + } + }, + "included": [ + { + "id": "a6ecb8ab-98d6-4cab-8487-ce9dd7be082b", + "type": "transport_event", + "attributes": { + "event": "container.transport.vessel_arrived", + "created_at": "2020-07-28T23:12:53Z", + "voyage_number": null, + "timestamp": "2020-07-28T23:12:53Z", + "timezone": "America/Los_Angeles" + }, + "relationships": { + "shipment": { + "data": { + "id": "1fc35241-4c8b-420d-803a-9e6661720a05", + "type": "shipment" + } + }, + "container": { + "data": { + "id": "8c2f335a-b155-4021-87f0-9b040159a981", + "type": "container" + } + }, + "vessel": { + "data": { + "id": "b381c692-8dad-4f04-873f-d9e567143335", + "type": "vessel" + } + }, + "location": { + "data": { + "id": "f5a8a49f-d8b2-4d2a-8a43-0e4ff0ce7995", + "type": "port" + } + }, + "terminal": { + "data": { + "id": "26fede8d-2c6d-4bf5-98d6-5a86d30f17a9", + "type": "terminal" + } + } + } + }, + { + "id": "f5a8a49f-d8b2-4d2a-8a43-0e4ff0ce7995", + "type": "port", + "attributes": { + "id": "f5a8a49f-d8b2-4d2a-8a43-0e4ff0ce7995", + "name": "Port of Oakland", + "code": "USOAK", + "state_abbr": "CA", + "city": "Oakland", + "country_code": "US", + "time_zone": "America/Los_Angeles" + } + }, + { + "id": "1fc35241-4c8b-420d-803a-9e6661720a05", + "type": "shipment", + "attributes": { + "created_at": "2020-07-28T23:12:53Z", + "bill_of_lading_number": "TE491846459E", + "ref_numbers": [ + null + ], + "shipping_line_scac": "MSCU", + "shipping_line_name": "Mediterranean Shipping Company", + "port_of_lading_locode": "MXZLO", + "port_of_lading_name": "Manzanillo", + "port_of_discharge_locode": "USOAK", + "port_of_discharge_name": "Port of Oakland", + "pod_vessel_name": "MSC CHANNE", + "pod_vessel_imo": "9710438", + "pod_voyage_number": "098N", + "destination_locode": null, + "destination_name": null, + "destination_timezone": null, + "destination_ata_at": null, + "destination_eta_at": null, + "pol_etd_at": null, + "pol_atd_at": "2020-07-15T23:12:53Z", + "pol_timezone": "America/Mexico_City", + "pod_eta_at": "2020-08-04T23:12:53Z", + "pod_ata_at": null, + "pod_timezone": "America/Los_Angeles" + }, + "relationships": { + "port_of_lading": { + "data": { + "id": "06564cb7-77d6-4e0e-8e4a-37756ca21bc9", + "type": "port" + } + }, + "port_of_discharge": { + "data": { + "id": "f5a8a49f-d8b2-4d2a-8a43-0e4ff0ce7995", + "type": "port" + } + }, + "pod_terminal": { + "data": { + "id": "06f5d3bb-f258-4f1b-8c2f-db78248f6e29", + "type": "terminal" + } + }, + "destination": { + "data": null + }, + "containers": { + "data": [ + { + "id": "8c2f335a-b155-4021-87f0-9b040159a981", + "type": "container" + } + ] + } + }, + "links": { + "self": "/v2/shipments/1fc35241-4c8b-420d-803a-9e6661720a05" + } + } + ] + } + }, + "Container Updated Event": { + "value": { + "data": { + "id": "416e293f-4423-47f7-abf3-1ae97054f41f", + "type": "webhook_notification", + "attributes": { + "id": "416e293f-4423-47f7-abf3-1ae97054f41f", + "event": "container.updated", + "delivery_status": "pending", + "created_at": "2020-06-04T22:03:09Z" + }, + "relationships": { + "reference_object": { + "data": { + "id": "fc48cb10-b7a8-47a4-a12f-89bce7434978", + "type": "container_updated_event" + } + }, + "webhook": { + "data": { + "id": "cda37836-aa40-455e-8b43-5fd74930c7f6", + "type": "webhook" + } + }, + "webhook_notification_logs": { + "data": [] + } + } + }, + "included": [ + { + "id": "fc48cb10-b7a8-47a4-a12f-89bce7434978", + "type": "container_updated_event", + "attributes": { + "changeset": { + "available_for_pickup": [ + false, + true + ], + "pod_terminal_holds": [ + null, + [ + { + "name": "customs", + "status": "hold", + "description": "CUST EXAM" + } + ] + ] + }, + "timestamp": "2020-06-04T22:03:09Z", + "timezone": "America/Los_Angeles" + }, + "relationships": { + "container": { + "data": { + "id": "1445af31-991c-4d52-a183-6c3ea97cd6e8", + "type": "container" + } + }, + "terminal": { + "data": { + "id": "07db0258-1911-4acf-8e70-0cfe4b100f80", + "type": "terminal" + } + } + } + }, + { + "id": "1445af31-991c-4d52-a183-6c3ea97cd6e8", + "type": "container", + "attributes": { + "number": "GLDU1355602", + "seal_number": "431ac97412228532", + "created_at": "2020-05-04T22:03:09Z", + "equipment_type": "dry", + "equipment_length": 40, + "equipment_height": "standard", + "weight_in_lbs": 55634, + "fees_at_pod_terminal": [], + "holds_at_pod_terminal": [], + "pickup_lfd": null, + "availability_known": true, + "available_for_pickup": null, + "pod_arrived_at": "2020-06-04T22:03:08Z", + "pod_discharged_at": "2020-06-04T22:03:08Z", + "final_destination_full_out_at": "2020-06-04T22:03:08Z", + "pod_full_out_at": null, + "empty_terminated_at": null, + "pod_timezone": "America/Los_Angeles", + "final_destination_timezone": null, + "empty_terminated_timezone": null + }, + "relationships": { + "shipment": { + "data": null + } + } + }, + { + "id": "07db0258-1911-4acf-8e70-0cfe4b100f80", + "type": "terminal", + "attributes": { + "id": "07db0258-1911-4acf-8e70-0cfe4b100f80", + "nickname": "Denesik-Hintz", + "name": "Adams LLC Terminal", + "firms_code": "E005" + }, + "relationships": { + "port": { + "data": { + "id": "d8a92775-95f9-47be-a6d2-42542a32d5fc", + "type": "port" + } + } + } + } + ] + } + } + } + } + } + } + }, + "operationId": "get-webhook-notification-id", + "description": "\n", + "tags": [ + "Webhook Notifications" + ], + "parameters": [ + { + "schema": { + "type": "string" + }, + "in": "query", + "description": "Comma delimited list of relations to include.", + "name": "include" + } + ] + } + }, + "/webhook_notifications": { + "get": { + "summary": "List webhook notifications", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/webhook_notification" + } + }, + "links": { + "$ref": "#/components/schemas/links" + }, + "meta": { + "$ref": "#/components/schemas/meta" + }, + "included": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/webhook" + }, + { + "$ref": "#/components/schemas/tracking_request" + }, + { + "$ref": "#/components/schemas/transport_event" + }, + { + "$ref": "#/components/schemas/estimated_event" + } + ] + } + } + } + } + } + } + } + }, + "operationId": "get-webhook-notifications", + "description": "Return the list of webhook notifications. This can be useful for reconciling your data if your endpoint has been down. ", + "tags": [ + "Webhook Notifications" + ], + "parameters": [ + { + "schema": { + "type": "integer" + }, + "in": "query", + "name": "page[number]" + }, + { + "schema": { + "type": "integer" + }, + "in": "query", + "name": "page[size]" + }, + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "include", + "description": "Comma delimited list of relations to include." + } + ] + } + }, + "/webhook_notifications/examples": { + "get": { + "summary": "Get webhook notification payload examples", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/webhook_notification" + } + }, + "links": { + "$ref": "#/components/schemas/links" + }, + "meta": { + "$ref": "#/components/schemas/meta" + }, + "included": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/webhook" + }, + { + "$ref": "#/components/schemas/tracking_request" + }, + { + "$ref": "#/components/schemas/transport_event" + }, + { + "$ref": "#/components/schemas/estimated_event" + } + ] + } + } + } + } + } + } + } + }, + "operationId": "get-webhook-notifications-example", + "description": "Returns an example payload as it would be sent to a webhook endpoint for the provided `event` ", + "tags": [ + "Webhook Notifications" + ], + "parameters": [ + { + "schema": { + "type": "string", + "enum": [ + "container.transport.vessel_arrived", + "container.transport.vessel_discharged", + "container.transport.vessel_loaded", + "container.transport.vessel_departed", + "container.transport.rail_departed", + "container.transport.rail_arrived", + "container.transport.rail_loaded", + "container.transport.rail_unloaded", + "container.transport.transshipment_arrived", + "container.transport.transshipment_discharged", + "container.transport.transshipment_loaded", + "container.transport.transshipment_departed", + "container.transport.feeder_arrived", + "container.transport.feeder_discharged", + "container.transport.feeder_loaded", + "container.transport.feeder_departed", + "container.transport.empty_out", + "container.transport.full_in", + "container.transport.full_out", + "container.transport.empty_in", + "container.transport.vessel_berthed", + "shipment.estimated.arrival", + "tracking_request.succeeded", + "tracking_request.failed", + "tracking_request.awaiting_manifest", + "tracking_request.tracking_stopped", + "container.created", + "container.updated", + "container.pod_terminal_changed", + "container.transport.arrived_at_inland_destination", + "container.transport.estimated.arrived_at_inland_destination", + "container.pickup_lfd.changed" + ], + "example": "container.transport.full_out" + }, + "in": "query", + "name": "event", + "description": "The webhook notification event name you wish to see an example of" + } + ] + }, + "parameters": [] + }, + "/webhooks/ips": { + "get": { + "summary": "List webhook IPs", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "webhook_notification_ips": { + "type": "array", + "items": { + "type": "string", + "format": "ipv4" + } + }, + "last_updated": { + "type": "string", + "format": "date-time" + } + } + }, + "examples": { + "Example List of IPs": { + "value": { + "webhook_notification_ips": [ + "35.222.62.171", + "3.230.67.145", + "44.217.15.129" + ], + "last_updated": "2023-10-17T21:23:16Z" + } + } + } + } + } + } + }, + "operationId": "get-webhooks-ips", + "description": "Return the list of IPs used for sending webhook notifications. This can be useful for whitelisting the IPs on the firewall.", + "tags": [ + "Webhooks" + ], + "parameters": [] + } + }, + "/containers": { + "get": { + "summary": "List containers", + "tags": [ + "Containers" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/container" + } + }, + "included": { + "type": "array", + "items": { + "$ref": "#/components/schemas/shipment" + } + }, + "links": { + "$ref": "#/components/schemas/links" + }, + "meta": { + "$ref": "#/components/schemas/meta" + } + } + }, + "examples": { + "Example List of containers": { + "value": { + "data": [ + { + "id": "be0b247b-c144-4163-8919-cf9178930736", + "type": "container", + "attributes": { + "number": "TCLU6718159", + "seal_number": null, + "created_at": "2024-06-26T15:05:18Z", + "ref_numbers": [], + "pod_arrived_at": "2024-06-21T14:12:00Z", + "pod_discharged_at": "2024-06-23T00:19:00Z", + "final_destination_full_out_at": null, + "holds_at_pod_terminal": [], + "available_for_pickup": false, + "equipment_type": "dry", + "equipment_length": 40, + "equipment_height": "high_cube", + "weight_in_lbs": 53502, + "pod_full_out_at": "2024-06-26T16:15:00Z", + "empty_terminated_at": null, + "terminal_checked_at": "2024-06-26T18:47:56Z", + "fees_at_pod_terminal": [], + "pickup_lfd": null, + "pickup_appointment_at": null, + "pod_full_out_chassis_number": null, + "location_at_pod_terminal": "Community", + "pod_last_tracking_request_at": "2024-06-26T18:47:56Z", + "shipment_last_tracking_request_at": "2024-06-26T15:05:18Z", + "availability_known": true, + "pod_timezone": "America/New_York", + "final_destination_timezone": null, + "empty_terminated_timezone": "America/New_York", + "pod_rail_carrier_scac": null, + "ind_rail_carrier_scac": null, + "pod_rail_loaded_at": null, + "pod_rail_departed_at": null, + "ind_eta_at": null, + "ind_ata_at": null, + "ind_rail_unloaded_at": null, + "ind_facility_lfd_on": null + }, + "relationships": { + "shipment": { + "data": { + "id": "cc8f8e43-d6a9-4edb-a8c0-d0ab03c113d3", + "type": "shipment" + } + }, + "pickup_facility": { + "data": null + }, + "pod_terminal": { + "data": { + "id": "a243bdf8-0da3-4056-a6a7-05fe8ab43999", + "type": "terminal" + } + }, + "transport_events": { + "data": [ + { + "id": "fb53b35c-6a0c-4e22-b196-b623e8ba7db5", + "type": "transport_event" + }, + { + "id": "179897a3-04f5-450b-86e1-db57970c0248", + "type": "transport_event" + }, + { + "id": "a119b8e6-10c5-4967-8364-885f7dbf8e50", + "type": "transport_event" + }, + { + "id": "3372d58c-df89-46a2-b064-b308a9dc7040", + "type": "transport_event" + }, + { + "id": "800a3e59-8231-4e42-a80f-73c97cfb1be9", + "type": "transport_event" + }, + { + "id": "d466676b-0073-4b0c-89aa-d42486e9ed4f", + "type": "transport_event" + }, + { + "id": "85665836-5915-4cb9-ab78-b8487598cd0d", + "type": "transport_event" + }, + { + "id": "94cca22a-520a-4d19-a551-0554aceb3794", + "type": "transport_event" + }, + { + "id": "032e812f-d5e4-48af-8d79-2c2b41a07032", + "type": "transport_event" + }, + { + "id": "1b7dd74d-eff6-4e06-9a15-234295ce6fd5", + "type": "transport_event" + }, + { + "id": "b9f07b7e-1653-4209-b375-4588653d5275", + "type": "transport_event" + }, + { + "id": "a35f3c1b-ad35-4347-b0f2-9e08f0d4ca64", + "type": "transport_event" + } + ] + }, + "raw_events": { + "data": [ + { + "id": "e26fc659-f79d-4cc0-8efc-5ce5e8444891", + "type": "raw_event" + }, + { + "id": "8820af5e-cb77-41c5-a897-906f2c56eb1e", + "type": "raw_event" + }, + { + "id": "a628a2c2-dab6-4b04-b3ae-d7ec99098a89", + "type": "raw_event" + }, + { + "id": "6ca157f9-3b58-4db5-8155-fc7b41a62611", + "type": "raw_event" + }, + { + "id": "6fb06390-4b0a-4c1f-9703-ec89927df7f3", + "type": "raw_event" + }, + { + "id": "26fe408d-e091-412a-a2fb-23a4d778b6b9", + "type": "raw_event" + }, + { + "id": "16490dd8-d79f-468a-91b1-dbb30bb45c85", + "type": "raw_event" + }, + { + "id": "ff3db923-a644-4706-8057-1bd53c95fbd5", + "type": "raw_event" + }, + { + "id": "3fd27ffd-9618-477f-b1a9-cbc179defefe", + "type": "raw_event" + }, + { + "id": "f92efdd3-f79c-4a1c-97ce-9a47588b525c", + "type": "raw_event" + }, + { + "id": "3b2a88ef-df0b-4e61-99c1-d4a175910111", + "type": "raw_event" + }, + { + "id": "9b367e33-9e43-488f-8217-081698adf40d", + "type": "raw_event" + }, + { + "id": "ee0915df-e2f8-46b0-acf1-816871ca142d", + "type": "raw_event" + } + ] + } + } + }, + { + "id": "a9e52f3d-2fa9-467c-8deb-09e90dac2f0b", + "type": "container", + "attributes": { + "number": "TCLU2224327", + "seal_number": null, + "created_at": "2024-06-26T15:05:34Z", + "ref_numbers": [], + "pod_arrived_at": "2024-06-21T14:12:00Z", + "pod_discharged_at": "2024-06-23T17:21:00Z", + "final_destination_full_out_at": null, + "holds_at_pod_terminal": [], + "available_for_pickup": false, + "equipment_type": "dry", + "equipment_length": 20, + "equipment_height": "standard", + "weight_in_lbs": 44225, + "pod_full_out_at": null, + "empty_terminated_at": null, + "terminal_checked_at": "2024-06-26T18:47:56Z", + "fees_at_pod_terminal": [], + "pickup_lfd": null, + "pickup_appointment_at": null, + "pod_full_out_chassis_number": null, + "location_at_pod_terminal": "Yard", + "pod_last_tracking_request_at": "2024-06-26T18:47:56Z", + "shipment_last_tracking_request_at": "2024-06-26T15:05:34Z", + "availability_known": true, + "pod_timezone": "America/New_York", + "final_destination_timezone": "America/Chicago", + "empty_terminated_timezone": "America/Chicago", + "pod_rail_carrier_scac": "CSXT", + "ind_rail_carrier_scac": "CSXT", + "pod_rail_loaded_at": null, + "pod_rail_departed_at": null, + "ind_eta_at": "2024-07-02T14:20:00Z", + "ind_ata_at": null, + "ind_rail_unloaded_at": null, + "ind_facility_lfd_on": null + }, + "relationships": { + "shipment": { + "data": { + "id": "99f84294-3bda-4765-81b3-31765e6d2a24", + "type": "shipment" + } + }, + "pickup_facility": { + "data": { + "id": "e6fa9a01-511b-4f43-a7e1-d628315b84ef", + "type": "terminal" + } + }, + "pod_terminal": { + "data": { + "id": "a243bdf8-0da3-4056-a6a7-05fe8ab43999", + "type": "terminal" + } + }, + "transport_events": { + "data": [ + { + "id": "4a5a04b7-8974-4c46-beaa-bf55004422c9", + "type": "transport_event" + }, + { + "id": "11c90391-aa9c-408b-b20e-daed8dd09586", + "type": "transport_event" + }, + { + "id": "d7f23c71-7084-4fbb-9fef-740f624182aa", + "type": "transport_event" + }, + { + "id": "c4c3537d-5c47-4623-afe0-edc0dd6f75c5", + "type": "transport_event" + }, + { + "id": "e4002e13-0a74-4bd4-9147-787bb21e2fda", + "type": "transport_event" + }, + { + "id": "57b5568f-d8a6-443d-b1c8-be5cd080e5ed", + "type": "transport_event" + }, + { + "id": "0771cff1-79bf-4eaa-9d9d-790cb433ce44", + "type": "transport_event" + }, + { + "id": "031021b9-7b39-41f7-bd45-26cc8cb799d2", + "type": "transport_event" + }, + { + "id": "9956448d-34d3-4c23-bcfd-19807eb4034f", + "type": "transport_event" + }, + { + "id": "f1caf6ea-3e92-4b68-bcea-556828301062", + "type": "transport_event" + }, + { + "id": "2dd558a8-fa07-454c-acf3-b53d072264af", + "type": "transport_event" + }, + { + "id": "83118511-d9ed-4b16-b323-5e685d9b266e", + "type": "transport_event" + } + ] + }, + "raw_events": { + "data": [ + { + "id": "f256289f-219d-45f6-b727-56fb9c6bc433", + "type": "raw_event" + }, + { + "id": "c5581f60-ffb2-4ef0-a855-b70acd3b294f", + "type": "raw_event" + }, + { + "id": "3c6716f9-814c-4459-9bbb-753f010446b2", + "type": "raw_event" + }, + { + "id": "62c7b849-c99b-4e37-9861-105141cc0a4c", + "type": "raw_event" + }, + { + "id": "7db37d43-426a-4f84-82af-2957621ce466", + "type": "raw_event" + }, + { + "id": "d4342119-db56-46fc-8299-f573f5b52e73", + "type": "raw_event" + }, + { + "id": "ba159c3b-590c-43ff-bc04-b0354fd326f4", + "type": "raw_event" + }, + { + "id": "ec1be31a-17f1-4f6f-85ab-c74cb0dbe6cb", + "type": "raw_event" + }, + { + "id": "d12c4656-0d1f-4f30-a0fa-a2ee887741a8", + "type": "raw_event" + }, + { + "id": "146d56e2-b41d-4469-8202-0c7d7315e794", + "type": "raw_event" + }, + { + "id": "6b86b4d1-f85f-4c26-9fbc-3132a62f0fbc", + "type": "raw_event" + }, + { + "id": "bb2c9105-e421-4372-9265-e61b3fa54851", + "type": "raw_event" + }, + { + "id": "3a09c49f-c12e-49e9-b2de-b8dca2b3d608", + "type": "raw_event" + }, + { + "id": "54d91ebc-8f57-4764-b7c6-a3f4dc2459be", + "type": "raw_event" + }, + { + "id": "9e788c46-9431-41eb-baee-c8b14fb4f590", + "type": "raw_event" + }, + { + "id": "d3051802-8b6f-497d-ad16-fb35547c8662", + "type": "raw_event" + }, + { + "id": "af3090e7-c6d3-4d8e-88aa-9cd757102f9b", + "type": "raw_event" + }, + { + "id": "95daf204-d95d-4a89-bedf-358bedb8b3b8", + "type": "raw_event" + } + ] + } + } + }, + { + "id": "8d1faeeb-3890-4fac-8659-cd13737b26f1", + "type": "container", + "attributes": { + "number": "CMAU0619052", + "seal_number": null, + "created_at": "2024-06-26T15:02:11Z", + "ref_numbers": [], + "pod_arrived_at": "2024-06-22T22:10:00Z", + "pod_discharged_at": "2024-06-23T20:38:00Z", + "final_destination_full_out_at": null, + "holds_at_pod_terminal": [], + "available_for_pickup": false, + "equipment_type": "dry", + "equipment_length": 20, + "equipment_height": "standard", + "weight_in_lbs": null, + "pod_full_out_at": null, + "empty_terminated_at": null, + "terminal_checked_at": "2024-06-26T18:47:47Z", + "fees_at_pod_terminal": [], + "pickup_lfd": "2024-06-27T07:00:00Z", + "pickup_appointment_at": null, + "pod_full_out_chassis_number": null, + "location_at_pod_terminal": "Grounded", + "pod_last_tracking_request_at": "2024-06-26T18:47:47Z", + "shipment_last_tracking_request_at": "2024-06-26T15:02:11Z", + "availability_known": true, + "pod_timezone": "America/Los_Angeles", + "final_destination_timezone": "Asia/Shanghai", + "empty_terminated_timezone": "Asia/Shanghai", + "pod_rail_carrier_scac": null, + "ind_rail_carrier_scac": null, + "pod_rail_loaded_at": null, + "pod_rail_departed_at": null, + "ind_eta_at": null, + "ind_ata_at": null, + "ind_rail_unloaded_at": null, + "ind_facility_lfd_on": null + }, + "relationships": { + "shipment": { + "data": { + "id": "f706cbea-3264-473d-8d26-af257f3bc1be", + "type": "shipment" + } + }, + "pickup_facility": { + "data": null + }, + "pod_terminal": { + "data": { + "id": "eaa2580c-5f5b-4198-85e4-821145d62098", + "type": "terminal" + } + }, + "transport_events": { + "data": [ + { + "id": "1bb3a814-edab-403f-8ef2-a6d0966df423", + "type": "transport_event" + }, + { + "id": "19f197bf-444c-40ee-8478-6f02abe715a9", + "type": "transport_event" + }, + { + "id": "4c9223bb-0218-4175-8a2e-7bb99c40642a", + "type": "transport_event" + }, + { + "id": "432da964-6e99-45e9-b4b1-00b7be858591", + "type": "transport_event" + }, + { + "id": "b462b4d1-1e02-4037-af7f-6c8fa981f268", + "type": "transport_event" + }, + { + "id": "e3ca4a25-692f-474a-aa71-48fb9840aef1", + "type": "transport_event" + }, + { + "id": "014b9d1f-f033-4a3e-89f9-c57569883436", + "type": "transport_event" + }, + { + "id": "2cec71c9-721a-4060-993f-0ffcf01151cd", + "type": "transport_event" + }, + { + "id": "24941515-1b5e-4fca-87eb-092e56ed156a", + "type": "transport_event" + }, + { + "id": "0fd06bc0-1fda-467c-ab67-90a30c6d62ab", + "type": "transport_event" + }, + { + "id": "0bce915e-b9ba-42a0-a484-136266fe8b9a", + "type": "transport_event" + }, + { + "id": "582006aa-6547-4712-b964-5637aad839b4", + "type": "transport_event" + }, + { + "id": "11458420-b354-4288-a275-7572d3c60e33", + "type": "transport_event" + }, + { + "id": "5c900fa1-11bf-4f96-89e7-b12f6a98edc4", + "type": "transport_event" + } + ] + }, + "raw_events": { + "data": [ + { + "id": "999d7de7-eaed-4313-85df-772a6d24a85e", + "type": "raw_event" + }, + { + "id": "ac9c2780-240d-443f-87f4-95465fa5447b", + "type": "raw_event" + }, + { + "id": "a0ee3724-91c3-4b78-af32-2f16e8c2d600", + "type": "raw_event" + }, + { + "id": "79725b1f-19f7-4fc3-8c69-586e237c1719", + "type": "raw_event" + }, + { + "id": "4f07f257-ea4f-4e61-94ed-a01598899020", + "type": "raw_event" + }, + { + "id": "41612acb-b3d4-495f-9138-f34105851d21", + "type": "raw_event" + }, + { + "id": "84ffdd37-ec39-404a-9b68-d72d8fb96d48", + "type": "raw_event" + }, + { + "id": "7e9d8a6d-5339-4266-a6fb-22c28f41149f", + "type": "raw_event" + }, + { + "id": "2359b787-b218-42dc-b9a5-84b608aee671", + "type": "raw_event" + }, + { + "id": "ea33303e-0e48-4442-9886-0dfe38b726b5", + "type": "raw_event" + }, + { + "id": "3689d013-8525-418b-92ed-95ec684130b4", + "type": "raw_event" + }, + { + "id": "a64f7f7e-a6fa-4913-aba0-b28ba189d68b", + "type": "raw_event" + }, + { + "id": "c264a859-4fcb-4fab-95c0-29be99ec54a4", + "type": "raw_event" + }, + { + "id": "2aabf25c-9a3e-44bc-b6c8-ed7b1e3c3630", + "type": "raw_event" + }, + { + "id": "6ae70038-02d7-425a-99c1-8761c69a9033", + "type": "raw_event" + }, + { + "id": "c30021d8-93e9-4bfd-a978-e9e24de148c8", + "type": "raw_event" + } + ] + } + } + }, + { + "id": "853f1794-9b94-4118-9970-4e28e549440d", + "type": "container", + "attributes": { + "number": "TGHU6578122", + "seal_number": null, + "created_at": "2024-06-26T15:08:30Z", + "ref_numbers": [], + "pod_arrived_at": "2024-06-23T21:58:00Z", + "pod_discharged_at": "2024-06-24T02:28:00Z", + "final_destination_full_out_at": null, + "holds_at_pod_terminal": [], + "available_for_pickup": false, + "equipment_type": "dry", + "equipment_length": 40, + "equipment_height": "high_cube", + "weight_in_lbs": 8898, + "pod_full_out_at": null, + "empty_terminated_at": null, + "terminal_checked_at": "2024-06-26T18:47:47Z", + "fees_at_pod_terminal": [], + "pickup_lfd": "2024-06-27T07:00:00Z", + "pickup_appointment_at": null, + "pod_full_out_chassis_number": null, + "location_at_pod_terminal": "Wheeled", + "pod_last_tracking_request_at": "2024-06-26T18:47:46Z", + "shipment_last_tracking_request_at": "2024-06-26T15:08:30Z", + "availability_known": true, + "pod_timezone": "America/Los_Angeles", + "final_destination_timezone": "America/New_York", + "empty_terminated_timezone": "America/New_York", + "pod_rail_carrier_scac": "BNSF", + "ind_rail_carrier_scac": "CSXT", + "pod_rail_loaded_at": null, + "pod_rail_departed_at": null, + "ind_eta_at": "2024-07-07T08:00:00Z", + "ind_ata_at": null, + "ind_rail_unloaded_at": null, + "ind_facility_lfd_on": null + }, + "relationships": { + "shipment": { + "data": { + "id": "f3cfe624-706e-4a0c-89d5-140980d986fd", + "type": "shipment" + } + }, + "pickup_facility": { + "data": { + "id": "7e4557b9-cc5a-4298-aaec-1a32e90202c9", + "type": "terminal" + } + }, + "pod_terminal": { + "data": { + "id": "eaa2580c-5f5b-4198-85e4-821145d62098", + "type": "terminal" + } + }, + "transport_events": { + "data": [ + { + "id": "94f687d0-dc6b-4342-8710-cb98bc98716e", + "type": "transport_event" + }, + { + "id": "a8d0a842-95fe-4c12-a80e-bd12e87a1421", + "type": "transport_event" + }, + { + "id": "c1f1a186-3737-41ca-ae2b-f79a17519991", + "type": "transport_event" + }, + { + "id": "7fcc5496-d402-4b1c-a6c5-8514e6433070", + "type": "transport_event" + }, + { + "id": "ab7cd84a-edc5-476d-a1c4-190011582314", + "type": "transport_event" + }, + { + "id": "68d91384-3eb3-4d21-ac7a-c1f688f649c2", + "type": "transport_event" + }, + { + "id": "5a702f46-4356-4570-bd2e-2b8adab5ba3e", + "type": "transport_event" + } + ] + }, + "raw_events": { + "data": [ + { + "id": "a4b7e4b6-5227-4f34-9e91-9e75223798ae", + "type": "raw_event" + }, + { + "id": "4f3e3c7b-d1e6-4fde-b95e-ce9a8835445c", + "type": "raw_event" + }, + { + "id": "39e8570d-1a8f-4e10-8d2e-ac930abc5971", + "type": "raw_event" + }, + { + "id": "b8015a72-9741-4fbf-8adc-d30b87de6aa3", + "type": "raw_event" + }, + { + "id": "61512b1a-a94e-4cac-8bab-763588dbbddf", + "type": "raw_event" + }, + { + "id": "c0bf87f2-37c3-4895-90b0-9f97ac4b5c13", + "type": "raw_event" + }, + { + "id": "3856c7e7-f832-42ca-873b-096952599e29", + "type": "raw_event" + }, + { + "id": "485be998-0861-4d10-8a60-f66d27eb46a7", + "type": "raw_event" + }, + { + "id": "29cde194-bbd9-40c2-adba-5dcb1514f5fc", + "type": "raw_event" + } + ] + } + } + }, + { + "id": "681d713d-bcd6-4303-b082-b9f893e7d1d9", + "type": "container", + "attributes": { + "number": "CSNU8439129", + "seal_number": null, + "created_at": "2024-06-26T15:02:32Z", + "ref_numbers": [], + "pod_arrived_at": "2024-06-23T21:58:00Z", + "pod_discharged_at": "2024-06-26T04:30:00Z", + "final_destination_full_out_at": null, + "holds_at_pod_terminal": [], + "available_for_pickup": true, + "equipment_type": "dry", + "equipment_length": 40, + "equipment_height": "high_cube", + "weight_in_lbs": 40488, + "pod_full_out_at": null, + "empty_terminated_at": null, + "terminal_checked_at": "2024-06-26T18:47:47Z", + "fees_at_pod_terminal": [], + "pickup_lfd": "2024-07-01T07:00:00Z", + "pickup_appointment_at": "2024-06-28T15:00:00Z", + "pod_full_out_chassis_number": null, + "location_at_pod_terminal": "Grounded", + "pod_last_tracking_request_at": "2024-06-26T18:47:46Z", + "shipment_last_tracking_request_at": "2024-06-26T15:02:32Z", + "availability_known": true, + "pod_timezone": "America/Los_Angeles", + "final_destination_timezone": "America/Chicago", + "empty_terminated_timezone": "America/Chicago", + "pod_rail_carrier_scac": "BNSF", + "ind_rail_carrier_scac": "BNSF", + "pod_rail_loaded_at": null, + "pod_rail_departed_at": null, + "ind_eta_at": "2024-07-04T22:00:00Z", + "ind_ata_at": null, + "ind_rail_unloaded_at": null, + "ind_facility_lfd_on": null + }, + "relationships": { + "shipment": { + "data": { + "id": "edd626cf-b0b5-4679-8a6c-80c8e9fe7698", + "type": "shipment" + } + }, + "pickup_facility": { + "data": { + "id": "572b372f-21c7-4403-8fb0-948377c74642", + "type": "terminal" + } + }, + "pod_terminal": { + "data": { + "id": "eaa2580c-5f5b-4198-85e4-821145d62098", + "type": "terminal" + } + }, + "transport_events": { + "data": [ + { + "id": "9d19125a-2944-442c-8132-bf0d83670e5c", + "type": "transport_event" + }, + { + "id": "b2ff0b47-3151-41e2-8c46-7f95cdbe4167", + "type": "transport_event" + }, + { + "id": "e52feaa3-7a50-4570-a2ea-bf06f955ce23", + "type": "transport_event" + }, + { + "id": "c56d95e5-774f-432c-b6f4-c53967f07292", + "type": "transport_event" + }, + { + "id": "9b1d8b48-e870-46fa-bc46-10f9b30c64d4", + "type": "transport_event" + }, + { + "id": "3e1d3571-6b24-4ad2-a071-4e70d59af521", + "type": "transport_event" + }, + { + "id": "60f5eefe-13d5-4f85-9b65-d13b4c67115a", + "type": "transport_event" + } + ] + }, + "raw_events": { + "data": [ + { + "id": "2af51127-0971-4741-9b97-ea1b338e3a7c", + "type": "raw_event" + }, + { + "id": "4472ded6-eb69-4f21-8666-9a4f2342dfeb", + "type": "raw_event" + }, + { + "id": "ce238754-d5fd-4dc8-9f55-2ac6efdfbb5e", + "type": "raw_event" + }, + { + "id": "5cd3b5ef-843f-4f60-87ce-5beaeea86f7b", + "type": "raw_event" + }, + { + "id": "55291737-98b6-403c-9424-1605e6e01007", + "type": "raw_event" + }, + { + "id": "cdc55ea8-a075-45b3-9570-ade6fb2f0d94", + "type": "raw_event" + }, + { + "id": "c0ab5406-4e10-4fdc-85a4-e17ce8d956f5", + "type": "raw_event" + }, + { + "id": "965f0172-1c20-4342-a44a-7be0e594ff76", + "type": "raw_event" + }, + { + "id": "db178011-c795-42a4-9537-b4e77ffb4f98", + "type": "raw_event" + } + ] + } + } + } + ], + "meta": { + "size": 5, + "total": 59229 + }, + "links": { + "self": "https://api.terminal49.com/v2/containers?page[size]=5", + "current": "https://api.terminal49.com/v2/containers?page[number]=1&page[size]=5", + "next": "https://api.terminal49.com/v2/containers?page[number]=2&page[size]=5", + "last": "https://api.terminal49.com/v2/containers?page[number]=11846&page[size]=5" + } + } + } + } + } + } + } + }, + "operationId": "get-containers", + "description": "Returns a list of container. The containers are returned sorted by creation date, with the most recently refreshed containers appearing first.\n\nThis API will return all containers associated with the account.", + "parameters": [ + { + "schema": { + "type": "integer", + "default": 1 + }, + "in": "query", + "name": "page[number]" + }, + { + "schema": { + "type": "integer", + "default": 30 + }, + "in": "query", + "name": "page[size]", + "description": "" + }, + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "include", + "description": "Comma delimited list of relations to include" + }, + { + "schema": { + "type": "integer" + }, + "in": "query", + "name": "terminal_checked_before", + "description": "Number of seconds in which containers were refreshed" + } + ] + }, + "patch": { + "summary": "Edit a container", + "operationId": "patch-containers-id", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/container" + } + } + } + } + } + } + }, + "description": "Update a container", + "tags": [ + "Containers" + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "required": [ + "attributes", + "type" + ], + "properties": { + "attributes": { + "type": "object", + "properties": { + "ref_numbers": { + "type": "array", + "items": { + "type": "string", + "example": "REF-12345" + } + } + } + } + } + } + } + } + } + } + } + } + }, + "/containers/{id}": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "id", + "in": "path", + "required": true + } + ], + "get": { + "summary": "Get a container", + "tags": [ + "Containers" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/container" + }, + "included": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/shipment" + }, + { + "$ref": "#/components/schemas/terminal" + }, + { + "$ref": "#/components/schemas/transport_event" + } + ] + } + } + } + }, + "examples": { + "Example Container": { + "value": { + "data": { + "id": "55a700e4-7005-45a9-92fd-1ff38641dbd9", + "type": "container", + "attributes": { + "number": "CAIU7432986", + "seal_number": null, + "created_at": "2024-06-26T15:05:21Z", + "ref_numbers": [], + "pod_arrived_at": null, + "pod_discharged_at": "2024-06-22T04:00:00Z", + "final_destination_full_out_at": null, + "holds_at_pod_terminal": [], + "available_for_pickup": true, + "equipment_type": "dry", + "equipment_length": 40, + "equipment_height": "high_cube", + "weight_in_lbs": null, + "pod_full_out_at": null, + "empty_terminated_at": null, + "terminal_checked_at": "2024-06-26T17:51:12Z", + "fees_at_pod_terminal": [], + "pickup_lfd": "2024-07-07T04:00:00Z", + "pickup_appointment_at": null, + "pod_full_out_chassis_number": null, + "location_at_pod_terminal": "Yard - Y0709A", + "pod_last_tracking_request_at": "2024-06-26T17:51:12Z", + "shipment_last_tracking_request_at": "2024-06-26T15:05:20Z", + "availability_known": true, + "pod_timezone": "America/New_York", + "final_destination_timezone": "US/Eastern", + "empty_terminated_timezone": "US/Eastern", + "pod_rail_carrier_scac": "CSXT", + "ind_rail_carrier_scac": "CSXT", + "pod_rail_loaded_at": null, + "pod_rail_departed_at": null, + "ind_eta_at": null, + "ind_ata_at": null, + "ind_rail_unloaded_at": null, + "ind_facility_lfd_on": null + }, + "relationships": { + "shipment": { + "data": { + "id": "02b1bd6f-407c-45bb-8645-06e7ee34e7e3", + "type": "shipment" + } + }, + "pickup_facility": { + "data": null + }, + "pod_terminal": { + "data": { + "id": "b859f5c3-8515-41da-bf20-39c0a5ada887", + "type": "terminal" + } + }, + "transport_events": { + "data": [ + { + "id": "45b542cb-332b-4684-b915-42e3a0759823", + "type": "transport_event" + }, + { + "id": "174ed528-a1a9-4002-aef0-f2c9369199da", + "type": "transport_event" + }, + { + "id": "7a2f30a6-a756-4c14-9477-fbfc1c7fe2f8", + "type": "transport_event" + }, + { + "id": "e7365004-175a-46e8-96cd-dbed0f3daf21", + "type": "transport_event" + }, + { + "id": "7c567bf3-7f01-4a3d-a176-eaa1f7165585", + "type": "transport_event" + } + ] + }, + "raw_events": { + "data": [ + { + "id": "2956f71c-bfb9-4e49-b9e2-1b4d53c74cac", + "type": "raw_event" + }, + { + "id": "391e0eda-65b5-4fc3-a53d-25ecd9570259", + "type": "raw_event" + }, + { + "id": "74810c04-6c8a-4194-8cff-52936584a965", + "type": "raw_event" + }, + { + "id": "4b1500e2-b23b-4896-87bd-c38b1d16f385", + "type": "raw_event" + }, + { + "id": "8b9a7d88-720a-4304-8c1e-a3336e39f481", + "type": "raw_event" + }, + { + "id": "bf1f59c5-5dd8-4013-87f9-d7056bc87114", + "type": "raw_event" + } + ] + } + } + }, + "links": { + "self": "https://api.terminal49.com/v2/containers/55a700e4-7005-45a9-92fd-1ff38641dbd9" + } + } + } + } + } + } + } + }, + "operationId": "get-containers-id", + "description": "Retrieves the details of a container.", + "parameters": [ + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "include", + "description": "Comma delimited list of relations to include" + } + ] + } + }, + "/containers/{id}/raw_events": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "id", + "in": "path", + "required": true + } + ], + "get": { + "summary": "Get a container's raw events", + "tags": [ + "Containers" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/raw_event" + } + } + } + }, + "examples": { + "Example Raw Events": { + "value": { + "data": [ + { + "id": "ca6b760f-13e9-4bf6-ab49-3cf2e40757fb", + "type": "raw_event", + "attributes": { + "timestamp": "2020-03-03T00:00:00Z", + "estimated": false, + "actual_on": "2020-03-03", + "estimated_at": null, + "actual_at": null, + "event": "empty_out", + "index": 0, + "original_event": "Truck Gate out empty", + "created_at": "2020-04-18T00:18:27Z", + "voyage_number": null, + "location_name": "Oakland", + "location_locode": null, + "vessel_name": null, + "vessel_imo": null, + "timezone": null + }, + "relationships": { + "location": { + "data": null + }, + "vessel": { + "data": null + } + } + }, + { + "id": "bcdfc796-0570-4c85-9336-d6c7d0da02d2", + "type": "raw_event", + "attributes": { + "timestamp": "2020-03-09T00:00:00Z", + "estimated": false, + "actual_on": "2020-03-09", + "estimated_at": null, + "actual_at": null, + "event": "full_in", + "index": 1, + "original_event": "Truck Arrival in", + "created_at": "2020-04-18T00:18:27Z", + "voyage_number": null, + "location_name": null, + "location_locode": null, + "vessel_name": null, + "vessel_imo": null, + "timezone": null + }, + "relationships": { + "location": { + "data": null + }, + "vessel": { + "data": null + } + } + }, + { + "id": "a4ff01b0-b374-4123-ae65-3dc0c7ea41ea", + "type": "raw_event", + "attributes": { + "timestamp": "2020-03-14T00:00:00Z", + "estimated": false, + "actual_on": "2020-03-14", + "estimated_at": null, + "actual_at": null, + "event": "vessel_loaded", + "index": 2, + "original_event": "Vessel Loaded", + "created_at": "2020-04-18T00:18:27Z", + "voyage_number": "FA009R", + "location_name": null, + "location_locode": null, + "vessel_name": "MSC FAITH", + "vessel_imo": null, + "timezone": null + }, + "relationships": { + "location": { + "data": null + }, + "vessel": { + "data": { + "id": "4b473d0e-7073-4171-8b5b-15e71e9e13cc", + "type": "vessel" + } + } + } + }, + { + "id": "ca5862ef-6e27-4245-a281-0cec6bbe1fb7", + "type": "raw_event", + "attributes": { + "timestamp": "2020-03-15T00:00:00Z", + "estimated": false, + "actual_on": "2020-03-15", + "estimated_at": null, + "actual_at": null, + "event": "vessel_departed", + "index": 3, + "original_event": "Vessel departed", + "created_at": "2020-04-18T00:18:27Z", + "voyage_number": "FA009R", + "location_name": null, + "location_locode": null, + "vessel_name": "MSC FAITH", + "vessel_imo": null, + "timezone": null + }, + "relationships": { + "location": { + "data": null + }, + "vessel": { + "data": { + "id": "4b473d0e-7073-4171-8b5b-15e71e9e13cc", + "type": "vessel" + } + } + } + }, + { + "id": "f47a903e-e6d1-41c5-aec6-8401b2abf297", + "type": "raw_event", + "attributes": { + "timestamp": "2020-03-25T00:00:00Z", + "estimated": false, + "actual_on": "2020-03-25", + "estimated_at": null, + "actual_at": null, + "event": "transshipment_arrived", + "index": 4, + "original_event": "Vessel arrived", + "created_at": "2020-04-18T00:18:27Z", + "voyage_number": "FA009R", + "location_name": null, + "location_locode": null, + "vessel_name": "MSC FAITH", + "vessel_imo": null, + "timezone": null + }, + "relationships": { + "location": { + "data": null + }, + "vessel": { + "data": { + "id": "4b473d0e-7073-4171-8b5b-15e71e9e13cc", + "type": "vessel" + } + } + } + }, + { + "id": "72a1a13b-a2e0-4ac0-851d-eec41e9e9087", + "type": "raw_event", + "attributes": { + "timestamp": "2020-03-25T00:00:00Z", + "estimated": false, + "actual_on": "2020-03-25", + "estimated_at": null, + "actual_at": null, + "event": "transshipment_discharged", + "index": 5, + "original_event": "Vessel Discharged", + "created_at": "2020-04-18T00:18:27Z", + "voyage_number": "FA009R", + "location_name": null, + "location_locode": null, + "vessel_name": "MSC FAITH", + "vessel_imo": null, + "timezone": null + }, + "relationships": { + "location": { + "data": null + }, + "vessel": { + "data": { + "id": "4b473d0e-7073-4171-8b5b-15e71e9e13cc", + "type": "vessel" + } + } + } + }, + { + "id": "cd91f0cf-ee73-4c47-b99f-63245cb5bc96", + "type": "raw_event", + "attributes": { + "timestamp": "2020-04-07T00:00:00Z", + "estimated": false, + "actual_on": "2020-04-07", + "estimated_at": null, + "actual_at": null, + "event": "transshipment_loaded", + "index": 6, + "original_event": "Vessel Loaded", + "created_at": "2020-04-18T00:18:27Z", + "voyage_number": "15W10", + "location_name": null, + "location_locode": null, + "vessel_name": "SINGAPORE EXPRESS", + "vessel_imo": null, + "timezone": null + }, + "relationships": { + "location": { + "data": null + }, + "vessel": { + "data": { + "id": "345c05ab-4217-4ffe-a1a4-6c03b9ad2b36", + "type": "vessel" + } + } + } + }, + { + "id": "561dbb7e-c3ab-4e63-b09b-957878b1425f", + "type": "raw_event", + "attributes": { + "timestamp": "2020-04-07T00:00:00Z", + "estimated": false, + "actual_on": "2020-04-07", + "estimated_at": null, + "actual_at": null, + "event": "transshipment_departed", + "index": 7, + "original_event": "Vessel departed", + "created_at": "2020-04-18T00:18:27Z", + "voyage_number": "15W10", + "location_name": null, + "location_locode": null, + "vessel_name": "SINGAPORE EXPRESS", + "vessel_imo": null, + "timezone": null + }, + "relationships": { + "location": { + "data": null + }, + "vessel": { + "data": { + "id": "345c05ab-4217-4ffe-a1a4-6c03b9ad2b36", + "type": "vessel" + } + } + } + }, + { + "id": "551711a6-62ad-4205-8da2-00e0c0cbd2db", + "type": "raw_event", + "attributes": { + "timestamp": "2020-04-12T00:00:00Z", + "estimated": false, + "actual_on": "2020-04-12", + "estimated_at": null, + "actual_at": null, + "event": "vessel_arrived", + "index": 8, + "original_event": "Vessel arrived", + "created_at": "2020-04-18T00:18:27Z", + "voyage_number": "15W10", + "location_name": null, + "location_locode": null, + "vessel_name": "SINGAPORE EXPRESS", + "vessel_imo": null, + "timezone": null + }, + "relationships": { + "location": { + "data": null + }, + "vessel": { + "data": { + "id": "345c05ab-4217-4ffe-a1a4-6c03b9ad2b36", + "type": "vessel" + } + } + } + }, + { + "id": "f4027470-75ca-4e2a-b4f0-47654a25ac48", + "type": "raw_event", + "attributes": { + "timestamp": "2020-04-13T00:00:00Z", + "estimated": false, + "actual_on": "2020-04-13", + "estimated_at": null, + "actual_at": null, + "event": "vessel_discharged", + "index": 9, + "original_event": "Vessel Discharged", + "created_at": "2020-04-18T00:18:27Z", + "voyage_number": "15W10", + "location_name": null, + "location_locode": null, + "vessel_name": "SINGAPORE EXPRESS", + "vessel_imo": null, + "timezone": null + }, + "relationships": { + "location": { + "data": null + }, + "vessel": { + "data": { + "id": "345c05ab-4217-4ffe-a1a4-6c03b9ad2b36", + "type": "vessel" + } + } + } + }, + { + "id": "50f11e4f-411e-48e2-8141-64226500df9c", + "type": "raw_event", + "attributes": { + "timestamp": "2020-04-14T00:00:00Z", + "estimated": false, + "actual_on": "2020-04-14", + "estimated_at": null, + "actual_at": null, + "event": "full_out", + "index": 10, + "original_event": "Truck Departure from", + "created_at": "2020-04-18T00:18:27Z", + "voyage_number": null, + "location_name": null, + "location_locode": null, + "vessel_name": null, + "vessel_imo": null, + "timezone": null + }, + "relationships": { + "location": { + "data": null + }, + "vessel": { + "data": null + } + } + }, + { + "id": "49aea23c-b8c5-4a97-b133-f7a9723fa1b4", + "type": "raw_event", + "attributes": { + "timestamp": "2020-04-15T00:00:00Z", + "estimated": false, + "actual_on": "2020-04-15", + "estimated_at": null, + "actual_at": null, + "event": "empty_in", + "index": 11, + "original_event": "Truck Gate in empty", + "created_at": "2020-04-18T00:18:27Z", + "voyage_number": null, + "location_name": null, + "location_locode": null, + "vessel_name": null, + "vessel_imo": null, + "timezone": null + }, + "relationships": { + "location": { + "data": null + }, + "vessel": { + "data": null + } + } + } + ], + "included": [ + { + "id": "4b473d0e-7073-4171-8b5b-15e71e9e13cc", + "type": "vessel", + "attributes": { + "name": "MSC FAITH", + "imo": null, + "mmsi": "636019213", + "latitude": 70.22625823437389, + "longitude": 45.06279126658865, + "nautical_speed_knots": 100, + "navigational_heading_degrees": 1, + "position_timestamp": "2023-06-05T19:46:18Z" + } + }, + { + "id": "345c05ab-4217-4ffe-a1a4-6c03b9ad2b36", + "type": "vessel", + "attributes": { + "name": "SINGAPORE EXPRESS", + "imo": null, + "mmsi": "477300500", + "latitude": 70.22625823437389, + "longitude": 45.06279126658865, + "nautical_speed_knots": 100, + "navigational_heading_degrees": 1, + "position_timestamp": "2023-06-05T19:46:18Z" + } + } + ] + } + } + } + } + } + } + }, + "operationId": "get-containers-id-raw_events", + "description": "#### Deprecation warning\nThe `raw_events` endpoint is provided as-is.\n\n For past events we recommend consuming `transport_events`.\n\n---\nGet a list of past and future (estimated) milestones for a container as reported by the carrier. Some of the data is normalized even though the API is called raw_events. \n\nNormalized attributes: `event` and `timestamp` timestamp. Not all of the `event` values have been normalized. You can expect the the events related to container movements to be normalized but there are cases where events are not normalized. \n\nFor past historical events we recommend consuming `transport_events`. Although there are fewer events here those events go through additional vetting and normalization to avoid false positives and get you correct data.", + "deprecated": true + } + }, + "/containers/{id}/transport_events": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "id", + "in": "path", + "required": true + } + ], + "get": { + "summary": "Get a container's transport events", + "tags": [ + "Containers" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/transport_event" + } + }, + "included": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/shipment" + }, + { + "$ref": "#/components/schemas/container" + }, + { + "$ref": "#/components/schemas/port" + }, + { + "$ref": "#/components/schemas/metro_area" + }, + { + "$ref": "#/components/schemas/terminal" + }, + { + "$ref": "#/components/schemas/rail_terminal" + }, + { + "$ref": "#/components/schemas/vessel" + } + ] + } + }, + "links": { + "$ref": "#/components/schemas/links" + }, + "meta": { + "$ref": "#/components/schemas/meta" + } + } + }, + "examples": { + "Example transport events": { + "value": { + "data": [ + { + "id": "efc3f3c1-cdc2-4a7d-a176-762ddec107b8", + "type": "transport_event", + "attributes": { + "event": "container.transport.vessel_loaded", + "created_at": "2021-01-05T08:41:12Z", + "voyage_number": "15W10", + "timestamp": null, + "location_locode": "CLSAI", + "timezone": "America/Santiago" + }, + "relationships": { + "shipment": { + "data": { + "id": "06264731-503e-498e-bc76-f90b87b31562", + "type": "shipment" + } + }, + "container": { + "data": { + "id": "eeafd337-72b5-4e5c-87cb-9ef83fa99cf4", + "type": "container" + } + }, + "vessel": { + "data": { + "id": "345c05ab-4217-4ffe-a1a4-6c03b9ad2b36", + "type": "vessel" + } + }, + "location": { + "data": { + "id": "0ad2cf2b-e694-4ccc-9cd2-40af0d1fa1b5", + "type": "port" + } + }, + "terminal": { + "data": null + } + } + }, + { + "id": "951058bd-2c3b-4bcc-94e1-9be2526b9687", + "type": "transport_event", + "attributes": { + "event": "container.transport.vessel_departed", + "created_at": "2021-01-05T08:41:11Z", + "voyage_number": "15W10", + "timestamp": null, + "location_locode": "CLSAI", + "timezone": "America/Santiago" + }, + "relationships": { + "shipment": { + "data": { + "id": "06264731-503e-498e-bc76-f90b87b31562", + "type": "shipment" + } + }, + "container": { + "data": { + "id": "eeafd337-72b5-4e5c-87cb-9ef83fa99cf4", + "type": "container" + } + }, + "vessel": { + "data": { + "id": "345c05ab-4217-4ffe-a1a4-6c03b9ad2b36", + "type": "vessel" + } + }, + "location": { + "data": { + "id": "0ad2cf2b-e694-4ccc-9cd2-40af0d1fa1b5", + "type": "port" + } + }, + "terminal": { + "data": null + } + } + }, + { + "id": "69af6795-56c2-4157-9a87-afd761cc85a0", + "type": "transport_event", + "attributes": { + "event": "container.transport.full_out", + "created_at": "2020-05-14T00:05:41Z", + "voyage_number": null, + "timestamp": "2020-04-14T00:00:00Z", + "location_locode": "USOAK", + "timezone": "America/Los_Angeles" + }, + "relationships": { + "shipment": { + "data": { + "id": "06264731-503e-498e-bc76-f90b87b31562", + "type": "shipment" + } + }, + "container": { + "data": { + "id": "eeafd337-72b5-4e5c-87cb-9ef83fa99cf4", + "type": "container" + } + }, + "vessel": { + "data": null + }, + "location": { + "data": { + "id": "42d1ba3a-f4b8-431d-a6fe-49fd748a59e7", + "type": "port" + } + }, + "terminal": { + "data": null + } + } + }, + { + "id": "68c3c29a-504a-4dbb-ad27-7194ef42d484", + "type": "transport_event", + "attributes": { + "event": "container.transport.vessel_discharged", + "created_at": "2020-05-14T00:05:41Z", + "voyage_number": "15W10", + "timestamp": "2020-04-13T00:00:00Z", + "location_locode": "USOAK", + "timezone": "America/Los_Angeles" + }, + "relationships": { + "shipment": { + "data": { + "id": "06264731-503e-498e-bc76-f90b87b31562", + "type": "shipment" + } + }, + "container": { + "data": { + "id": "eeafd337-72b5-4e5c-87cb-9ef83fa99cf4", + "type": "container" + } + }, + "vessel": { + "data": { + "id": "345c05ab-4217-4ffe-a1a4-6c03b9ad2b36", + "type": "vessel" + } + }, + "location": { + "data": { + "id": "42d1ba3a-f4b8-431d-a6fe-49fd748a59e7", + "type": "port" + } + }, + "terminal": { + "data": { + "id": "3e550f0e-ac2a-48fb-b242-5be45ecf2c78", + "type": "terminal" + } + } + } + }, + { + "id": "03349405-a9be-4f3e-abde-28f2cb3922bd", + "type": "transport_event", + "attributes": { + "event": "container.transport.vessel_arrived", + "created_at": "2020-05-14T00:05:41Z", + "voyage_number": "15W10", + "timestamp": "2020-04-13T01:24:00Z", + "location_locode": "USOAK", + "timezone": "America/Los_Angeles" + }, + "relationships": { + "shipment": { + "data": { + "id": "06264731-503e-498e-bc76-f90b87b31562", + "type": "shipment" + } + }, + "container": { + "data": { + "id": "eeafd337-72b5-4e5c-87cb-9ef83fa99cf4", + "type": "container" + } + }, + "vessel": { + "data": { + "id": "345c05ab-4217-4ffe-a1a4-6c03b9ad2b36", + "type": "vessel" + } + }, + "location": { + "data": { + "id": "42d1ba3a-f4b8-431d-a6fe-49fd748a59e7", + "type": "port" + } + }, + "terminal": { + "data": { + "id": "3e550f0e-ac2a-48fb-b242-5be45ecf2c78", + "type": "terminal" + } + } + } + }, + { + "id": "ba9f85b4-658d-4f23-9308-635964df8037", + "type": "transport_event", + "attributes": { + "event": "container.transport.empty_in", + "created_at": "2020-05-14T00:05:42Z", + "voyage_number": null, + "timestamp": "2020-04-15T00:00:00Z", + "location_locode": null, + "timezone": null + }, + "relationships": { + "shipment": { + "data": { + "id": "06264731-503e-498e-bc76-f90b87b31562", + "type": "shipment" + } + }, + "container": { + "data": { + "id": "eeafd337-72b5-4e5c-87cb-9ef83fa99cf4", + "type": "container" + } + }, + "vessel": { + "data": null + }, + "location": { + "data": null + }, + "terminal": { + "data": null + } + } + } + ], + "links": { + "self": "https://api.terminal49.com/v2/containers/eeafd337-72b5-4e5c-87cb-9ef83fa99cf4/transport_events", + "current": "https://api.terminal49.com/v2/containers/eeafd337-72b5-4e5c-87cb-9ef83fa99cf4/transport_events?page[number]=1" + } + } + } + } + } + } + } + }, + "operationId": "get-containers-id-transport_events", + "description": "Get a list of past transport events (canonical) for a container. All data has been normalized across all carriers. These are a verified subset of the raw events may also be sent as Webhook Notifications to a webhook endpoint.\n\nThis does not provide any estimated future events. See `container/:id/raw_events` endpoint for that. ", + "parameters": [ + { + "schema": { + "type": "string" + }, + "in": "query", + "name": "include", + "description": "Comma delimited list of relations to include" + } + ] + } + }, + "/shipping_lines": { + "get": { + "summary": "Shipping Lines", + "tags": [ + "Shipping Lines" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/shipping_line" + } + }, + "links": { + "$ref": "#/components/schemas/links" + } + } + } + } + } + } + }, + "operationId": "get-shipping_lines", + "description": "Return a list of shipping lines supported by Terminal49. \nN.B. There is no pagination for this endpoint." + } + }, + "/shipping_lines/{id}": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "id", + "in": "path", + "required": true + } + ], + "get": { + "summary": "Get a single shipping line", + "tags": [ + "Shipping Lines" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/shipping_line" + } + } + } + } + } + } + }, + "operationId": "get-shipping_lines-id", + "description": "Return the details of a single shipping line." + } + }, + "/metro_areas/{id}": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "id", + "in": "path", + "required": true + } + ], + "get": { + "summary": "Get a metro area using the un/locode or the id", + "tags": [ + "Metro Areas" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/metro_area" + } + } + } + } + } + } + }, + "operationId": "get-metro-area-id", + "description": "Return the details of a single metro area." + } + }, + "/ports/{id}": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "id", + "in": "path", + "required": true + } + ], + "get": { + "summary": "Get a port using the locode or the id", + "tags": [ + "Ports" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/port" + } + } + } + } + } + } + }, + "operationId": "get-port-id", + "description": "Return the details of a single port." + } + }, + "/vessels/{id}": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "id", + "in": "path", + "required": true + } + ], + "get": { + "summary": "Get a vessel using the id", + "tags": [ + "Vessels" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/vessel" + } + } + } + } + } + } + }, + "operationId": "get-vessels-id", + "description": "Returns a vessel by it's given identifier" + } + }, + "/vessels/{imo}": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "imo", + "in": "path", + "required": true + } + ], + "get": { + "summary": "Get a vessel using the imo", + "tags": [ + "Vessels" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/vessel" + } + } + } + } + } + } + }, + "operationId": "get-vessels-imo", + "description": "Returns a vessel by the given IMO number.", + "x-internal": true + } + }, + "/terminals/{id}": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "id", + "in": "path", + "required": true + } + ], + "get": { + "summary": "Get a terminal using the id", + "tags": [ + "Terminals" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/terminal" + } + } + } + } + } + } + }, + "operationId": "get-terminal-id", + "description": "Return the details of a single terminal." + } + }, + "/parties": { + "get": { + "description": "Get a list of parties", + "operationId": "list-parties", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/party" + } + }, + "links": { + "$ref": "#/components/schemas/links" + }, + "meta": { + "$ref": "#/components/schemas/meta" + } + } + } + } + } + } + }, + "parameters": [ + { + "schema": { + "type": "integer", + "default": 1 + }, + "in": "query", + "name": "page[number]" + }, + { + "schema": { + "type": "integer", + "default": 25 + }, + "in": "query", + "name": "page[size]", + "description": "" + } + ], + "tags": [ + "Parties" + ] + }, + "post": { + "description": "Creates a new party", + "operationId": "post-party", + "responses": { + "201": { + "description": "Party Created", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/party" + }, + "links": { + "$ref": "#/components/schemas/link-self" + } + } + }, + "examples": { + "New Party": { + "value": { + "data": { + "id": "ba4cb904-827f-4038-8e31-1e92b3356218", + "type": "party", + "attributes": { + "company_name": "COMPANY NAME" + } + }, + "links": { + "self": "/v2/parties/ba4cb904-827f-4038-8e31-1e92b3356218" + } + } + } + } + } + }, + "headers": {} + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "errors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "examples": { + "Error Examples": { + "value": { + "errors": [ + { + "status": "422", + "source": { + "pointer": "/data/attributes/company_name" + }, + "title": "Unprocessable Entity", + "detail": "Company name can't be blank" + }, + { + "status": "422", + "source": { + "pointer": "/data/attributes/customer" + }, + "title": "Unprocessable Entity", + "detail": "'XXXX' already exists in Account" + } + ] + } + } + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "required": [ + "attributes" + ], + "properties": { + "attributes": { + "type": "object", + "properties": { + "company_name": { + "type": "string", + "example": "COMPANY NAME", + "description": "The name of the company" + } + } + } + } + } + } + } + } + } + }, + "tags": [ + "Parties" + ] + } + }, + "/parties/{id}": { + "parameters": [ + { + "schema": { + "type": "string" + }, + "name": "id", + "in": "path", + "required": true + } + ], + "get": { + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/party" + }, + "links": { + "$ref": "#/components/schemas/link-self" + } + } + } + } + } + } + }, + "operationId": "get-parties-id", + "description": "Returns a party by it's given identifier", + "tags": [ + "Parties" + ] + }, + "patch": { + "description": "Updates a party", + "operationId": "edit-party", + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/party" + }, + "links": { + "$ref": "#/components/schemas/link-self" + } + } + } + } + } + }, + "422": { + "description": "Unprocessable Entity", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "errors": { + "type": "array", + "items": { + "$ref": "#/components/schemas/error" + } + } + } + }, + "examples": { + "Error Examples": { + "value": { + "errors": [ + { + "status": "422", + "source": { + "pointer": "/data/attributes/company_name" + }, + "title": "Unprocessable Entity", + "detail": "Company name can't be blank" + }, + { + "status": "422", + "source": { + "pointer": "/data/attributes/customer" + }, + "title": "Unprocessable Entity", + "detail": "'XXXX' already exists in Account" + } + ] + } + } + } + } + } + } + }, + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "object", + "required": [ + "attributes" + ], + "properties": { + "attributes": { + "type": "object", + "properties": { + "company_name": { + "type": "string", + "example": "COMPANY NAME", + "description": "The name of the company" + } + } + } + } + } + } + } + } + } + }, + "tags": [ + "Parties" + ] + } + } + }, + "x-tagGroups": [ + { + "name": "End Points", + "tags": [ + "Shipments", + "Containers", + "Tracking Requests", + "Webhooks", + "Webhook Notifications", + "Metro Areas" + ] + } + ], + "components": { + "schemas": { + "shipment": { + "title": "Shipment model", + "type": "object", + "x-examples": {}, + "description": "", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "relationships": { + "type": "object", + "properties": { + "destination": { + "type": "object", + "properties": { + "data": { + "type": "object", + "nullable": true, + "properties": { + "type": { + "type": "string", + "enum": [ + "port", + "metro_area" + ] + }, + "id": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "type", + "id" + ] + } + } + }, + "port_of_lading": { + "type": "object", + "properties": { + "data": { + "type": "object", + "nullable": true, + "properties": { + "type": { + "type": "string", + "enum": [ + "port" + ] + }, + "id": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "type", + "id" + ] + } + } + }, + "containers": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "container" + ] + }, + "id": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "type", + "id" + ] + } + } + } + }, + "port_of_discharge": { + "type": "object", + "properties": { + "data": { + "type": "object", + "nullable": true, + "properties": { + "type": { + "type": "string", + "enum": [ + "port" + ] + }, + "id": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "type", + "id" + ] + } + } + }, + "pod_terminal": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "terminal" + ] + }, + "id": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "type", + "id" + ] + } + } + }, + "destination_terminal": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "terminal", + "rail_terminal" + ] + }, + "id": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "type", + "id" + ] + } + } + }, + "line_tracking_stopped_by_user": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "user" + ] + }, + "id": { + "type": "string", + "format": "uuid" + } + }, + "required": [ + "type", + "id" + ] + } + } + } + } + }, + "attributes": { + "type": "object", + "properties": { + "bill_of_lading_number": { + "type": "string" + }, + "normalized_number": { + "type": "string", + "description": "The normalized version of the shipment number used for querying the carrier" + }, + "ref_numbers": { + "type": "array", + "items": { + "type": "string" + }, + "nullable": true + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "port_of_lading_locode": { + "type": "string", + "description": "UN/LOCODE", + "nullable": true + }, + "port_of_lading_name": { + "type": "string", + "nullable": true + }, + "port_of_discharge_locode": { + "type": "string", + "description": "UN/LOCODE", + "nullable": true + }, + "port_of_discharge_name": { + "type": "string", + "nullable": true + }, + "destination_locode": { + "type": "string", + "description": "UN/LOCODE", + "nullable": true + }, + "destination_name": { + "type": "string", + "nullable": true + }, + "shipping_line_scac": { + "type": "string" + }, + "shipping_line_name": { + "type": "string" + }, + "shipping_line_short_name": { + "type": "string" + }, + "customer_name": { + "type": "string", + "nullable": true + }, + "pod_vessel_name": { + "type": "string", + "nullable": true + }, + "pod_vessel_imo": { + "type": "string", + "nullable": true + }, + "pod_voyage_number": { + "type": "string", + "nullable": true + }, + "pol_etd_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "pol_atd_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "pod_eta_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "pod_original_eta_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "pod_ata_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "destination_eta_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "destination_ata_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "pol_timezone": { + "type": "string", + "description": "IANA tz", + "nullable": true + }, + "pod_timezone": { + "type": "string", + "description": "IANA tz", + "nullable": true + }, + "destination_timezone": { + "type": "string", + "description": "IANA tz", + "nullable": true + }, + "line_tracking_last_attempted_at": { + "type": "string", + "format": "date-time", + "description": "When Terminal49 last tried to update the shipment status from the shipping line", + "nullable": true + }, + "line_tracking_last_succeeded_at": { + "type": "string", + "format": "date-time", + "description": "When Terminal49 last successfully updated the shipment status from the shipping line", + "nullable": true + }, + "line_tracking_stopped_at": { + "type": "string", + "format": "date-time", + "description": "When Terminal49 stopped checking at the shipping line", + "nullable": true + }, + "line_tracking_stopped_reason": { + "type": "string", + "enum": [ + "all_containers_terminated", + "past_arrival_window", + "no_updates_at_line", + "cancelled_by_user", + "booking_cancelled", + null + ], + "description": "The reason Terminal49 stopped checking", + "nullable": true + } + }, + "required": [ + "bill_of_lading_number" + ] + }, + "type": { + "type": "string", + "enum": [ + "shipment" + ] + }, + "links": { + "type": "object", + "properties": { + "self": { + "type": "string", + "format": "uri" + } + }, + "required": [ + "self" + ] + } + }, + "required": [ + "id", + "type", + "attributes", + "relationships", + "links" + ] + }, + "meta": { + "title": "meta", + "type": "object", + "properties": { + "size": { + "type": "integer" + }, + "total": { + "type": "integer" + } + } + }, + "link-self": { + "title": "link", + "type": "object", + "properties": { + "self": { + "type": "string", + "format": "uri" + } + } + }, + "links": { + "title": "links", + "type": "object", + "properties": { + "last": { + "type": "string", + "format": "uri" + }, + "next": { + "type": "string", + "format": "uri" + }, + "prev": { + "type": "string", + "format": "uri" + }, + "first": { + "type": "string", + "format": "uri" + }, + "self": { + "type": "string", + "format": "uri" + } + } + }, + "container": { + "title": "Container model", + "type": "object", + "x-examples": { + "Example Container": { + "id": "ff77a822-23a7-4ccd-95ca-g534c071baaf3", + "type": "container", + "attributes": { + "number": "KOCU4959010", + "ref_numbers": [ + "REF-1", + "REF-2" + ], + "seal_number": "210084213", + "created_at": "2021-10-18T09:52:33Z", + "equipment_type": "dry", + "equipment_length": 40, + "equipment_height": "high_cube", + "weight_in_lbs": 20210, + "fees_at_pod_terminal": [], + "holds_at_pod_terminal": [], + "pickup_lfd": "2022-01-21T08:00:00Z", + "pickup_appointment_at": null, + "pod_full_out_chassis_number": "APMZ418805", + "location_at_pod_terminal": "Delivered 02/11/2022 14:18", + "availability_known": true, + "available_for_pickup": false, + "pod_arrived_at": "2022-01-03T10:30:00Z", + "pod_discharged_at": "2022-01-08T09:15:00Z", + "final_destination_full_out_at": null, + "pod_full_out_at": "2022-02-11T22:18:00Z", + "empty_terminated_at": null, + "terminal_checked_at": "2022-02-11T22:45:32Z", + "pod_rail_carrier_scac": "UPRR", + "ind_rail_carrier_scac": "CSXT", + "pod_timezone": "America/Los_Angeles", + "final_destination_timezone": null, + "empty_terminated_timezone": null, + "pod_last_tracking_request_at": "2022-02-11T22:40:00Z", + "shipment_last_tracking_request_at": "2022-02-11T22:40:00Z", + "pod_rail_loaded_at": "2022-02-11T22:18:00Z", + "pod_rail_departed_at": "2022-02-11T23:30:00Z", + "ind_eta_at": null, + "ind_ata_at": "2022-02-15T01:12:00Z", + "ind_rail_unloaded_at": "2022-02-15T07:54:00Z", + "ind_facility_lfd_on": null + }, + "relationships": { + "shipment": { + "data": { + "id": "x92acf88-c263-43ddf-b005-aca2a32d47f1", + "type": "shipment" + } + }, + "pod_terminal": { + "data": { + "id": "x551cac7-aff5-40a6-9c63-49facf19cc3df", + "type": "terminal" + } + }, + "pickup_facility": { + "data": { + "id": "d7d8d314-b02b-4caa-b04f-d3d4726f4107", + "type": "terminal" + } + }, + "transport_events": { + "data": [ + { + "id": "xecfe2d1-c498-4022-a9f8-ec56722e1215", + "type": "transport_event" + }, + { + "id": "2900a9b8-d9e2-4696-abd86-4a767b885d23", + "type": "transport_event" + }, + { + "id": "5ad0dce1-x78e4-464d-af5f-a36190428a2c", + "type": "transport_event" + }, + { + "id": "876575d5-5ede-40d6-a093-c3a4cfcxaa1c7", + "type": "transport_event" + }, + { + "id": "dc2a9d8f-75e6-43xa5-a04e-58458495f08c", + "type": "transport_event" + }, + { + "id": "50xd2ea1-01ac-473d-8a08-3b5d77d2b793", + "type": "transport_event" + }, + { + "id": "9d1f55xe3-6758-4be7-872a-30451ddd957e", + "type": "transport_event" + } + ] + }, + "raw_events": { + "data": [ + { + "id": "38084a1d-a2eb-434e-81ac3-606c89a61c4b", + "type": "raw_event" + }, + { + "id": "53680df3-93d5-4385-86c5-a33ee41b4c1f", + "type": "raw_event" + }, + { + "id": "7d9cdf70-51e8-4b75-a8229-f5d691495ab6", + "type": "raw_event" + }, + { + "id": "e62d41ac-8738-42e8-b582-35ef28ae88e2", + "type": "raw_event" + }, + { + "id": "1209172b-acd8-4ce0-8821-dbc4934208b3", + "type": "raw_event" + }, + { + "id": "4265ea5f-2b9a-436f-98fa-803d8ed49acb2", + "type": "raw_event" + }, + { + "id": "c3cb2eb7-6c0a-4db8-8742-517b97b175d5", + "type": "raw_event" + }, + { + "id": "b1959f36-a218-4b6e-863a9-2e0b4ad5159c", + "type": "raw_event" + } + ] + } + } + } + }, + "description": "Represents the equipment during a specific journey.", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "container" + ] + }, + "attributes": { + "type": "object", + "properties": { + "number": { + "type": "string" + }, + "ref_numbers": { + "type": "array", + "items": { + "type": "string" + } + }, + "equipment_type": { + "type": "string", + "enum": [ + "dry", + "reefer", + "open top", + "flat rack", + "bulk", + "tank", + null + ], + "nullable": true + }, + "equipment_length": { + "type": "integer", + "enum": [ + null, + 10, + 20, + 40, + 45 + ], + "nullable": true + }, + "equipment_height": { + "type": "string", + "enum": [ + "standard", + "high_cube", + null + ], + "nullable": true + }, + "weight_in_lbs": { + "type": "number", + "nullable": true + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "seal_number": { + "type": "string", + "nullable": true + }, + "pickup_lfd": { + "type": "string", + "format": "date-time", + "description": "The last free day for pickup before demmurage accrues. Corresponding timezone is pod_timezone.", + "nullable": true + }, + "pickup_appointment_at": { + "type": "string", + "format": "date-time", + "description": "When available the pickup appointment time at the terminal is returned.", + "nullable": true + }, + "availability_known": { + "type": "boolean", + "description": "Whether Terminal 49 is receiving availability status from the terminal." + }, + "available_for_pickup": { + "type": "boolean", + "description": "If availability_known is true, then whether container is available to be picked up at terminal.", + "nullable": true + }, + "pod_arrived_at": { + "type": "string", + "format": "date-time", + "description": "Time the vessel arrived at the POD", + "nullable": true + }, + "pod_discharged_at": { + "type": "string", + "format": "date-time", + "description": "Discharge time at the port of discharge", + "nullable": true + }, + "pod_full_out_at": { + "type": "string", + "format": "date-time", + "description": "Full Out time at port of discharge. Null for inland moves.", + "nullable": true + }, + "terminal_checked_at": { + "type": "string", + "format": "date-time", + "description": "When the terminal was last checked.", + "nullable": true + }, + "pod_full_out_chassis_number": { + "type": "string", + "description": "The chassis number used when container was picked up at POD (if available)", + "nullable": true + }, + "location_at_pod_terminal": { + "type": "string", + "description": "Location at port of discharge terminal", + "nullable": true + }, + "final_destination_full_out_at": { + "type": "string", + "format": "date-time", + "description": "Pickup time at final destination for inland moves.", + "nullable": true + }, + "empty_terminated_at": { + "type": "string", + "format": "date-time", + "description": "Time empty container was returned.", + "nullable": true + }, + "holds_at_pod_terminal": { + "type": "array", + "items": { + "$ref": "#/components/schemas/terminal_hold" + } + }, + "fees_at_pod_terminal": { + "type": "array", + "items": { + "$ref": "#/components/schemas/terminal_fee" + } + }, + "pod_timezone": { + "type": "string", + "description": "IANA tz. Applies to attributes pod_arrived_at, pod_discharged_at, pickup_appointment_at, pod_full_out_at.", + "nullable": true + }, + "final_destination_timezone": { + "type": "string", + "description": "IANA tz. Applies to attribute final_destination_full_out_at.", + "nullable": true + }, + "empty_terminated_timezone": { + "type": "string", + "description": "IANA tz. Applies to attribute empty_terminated_at.", + "nullable": true + }, + "pod_rail_carrier_scac": { + "type": "string", + "description": "The SCAC of the rail carrier for the pickup leg of the container's journey.(BETA)", + "nullable": true + }, + "ind_rail_carrier_scac": { + "type": "string", + "description": "The SCAC of the rail carrier for the delivery leg of the container's journey.(BETA)", + "nullable": true + }, + "pod_last_tracking_request_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "shipment_last_tracking_request_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "pod_rail_loaded_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "pod_rail_departed_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "ind_eta_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "ind_ata_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "ind_rail_unloaded_at": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "ind_facility_lfd_on": { + "type": "string", + "format": "date-time", + "nullable": true + } + } + }, + "relationships": { + "type": "object", + "properties": { + "shipment": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "shipment" + ] + } + } + } + } + }, + "pickup_facility": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "terminal" + ] + } + } + } + } + }, + "pod_terminal": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "terminal" + ] + } + } + } + } + }, + "transport_events": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "transport_event" + ] + } + } + } + } + } + }, + "raw_events": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "raw_event" + ] + } + } + } + } + } + } + } + } + }, + "required": [ + "id", + "type", + "attributes" + ] + }, + "port": { + "title": "Port model", + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "attributes": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "code": { + "type": "string", + "description": "UN/LOCODE" + }, + "state_abbr": { + "type": "string", + "x-stoplight": { + "id": "jixah1a0q3exs" + }, + "nullable": true + }, + "city": { + "type": "string", + "x-stoplight": { + "id": "657ij4boc7kyv" + }, + "nullable": true + }, + "country_code": { + "type": "string", + "description": "2 digit country code" + }, + "time_zone": { + "type": "string", + "description": "IANA tz" + }, + "latitude": { + "type": "number", + "x-stoplight": { + "id": "480os7a90z6kk" + }, + "nullable": true + }, + "longitude": { + "type": "number", + "x-stoplight": { + "id": "nfdetqgx5p1yv" + }, + "nullable": true + } + } + }, + "type": { + "type": "string", + "enum": [ + "port" + ] + } + }, + "required": [ + "id", + "type" + ] + }, + "shipping_line": { + "title": "Shipping line model", + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "attributes": { + "type": "object", + "required": [ + "scac", + "name", + "alternative_scacs", + "short_name", + "bill_of_lading_tracking_support", + "booking_number_tracking_support", + "container_number_tracking_support" + ], + "properties": { + "scac": { + "type": "string", + "minLength": 4, + "maxLength": 4 + }, + "name": { + "type": "string" + }, + "alternative_scacs": { + "type": "array", + "x-stoplight": { + "id": "jwf70hnip0xwb" + }, + "description": "Additional SCACs which will be accepted in tracking requests", + "items": { + "x-stoplight": { + "id": "nrqnwg5y2u0ni" + }, + "type": "string", + "minLength": 4, + "maxLength": 4 + } + }, + "short_name": { + "type": "string" + }, + "bill_of_lading_tracking_support": { + "type": "boolean" + }, + "booking_number_tracking_support": { + "type": "boolean" + }, + "container_number_tracking_support": { + "type": "boolean" + } + } + }, + "type": { + "type": "string", + "enum": [ + "shipping_line" + ] + } + }, + "required": [ + "id", + "attributes", + "type" + ] + }, + "account": { + "title": "Account model", + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "container" + ] + }, + "attributes": { + "type": "object", + "required": [ + "company_name" + ], + "properties": { + "company_name": { + "type": "string" + } + } + } + }, + "required": [ + "id", + "type", + "attributes" + ], + "x-examples": {} + }, + "error": { + "title": "Error model", + "type": "object", + "properties": { + "detail": { + "type": "string", + "nullable": true + }, + "title": { + "type": "string", + "nullable": true + }, + "source": { + "type": "object", + "nullable": true, + "properties": { + "pointer": { + "type": "string", + "nullable": true + }, + "parameter": { + "type": "string", + "nullable": true + } + } + }, + "code": { + "type": "string", + "nullable": true + }, + "status": { + "type": "string", + "nullable": true + }, + "meta": { + "type": "object", + "nullable": true, + "properties": { + "tracking_request_id": { + "type": "string", + "format": "uuid", + "nullable": true + } + } + } + }, + "required": [ + "title" + ] + }, + "metro_area": { + "title": "Metro area model", + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "attributes": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "code": { + "type": "string", + "description": "UN/LOCODE" + }, + "state_abbr": { + "type": "string", + "x-stoplight": { + "id": "j9yuwej2ym7yq" + }, + "nullable": true + }, + "country_code": { + "type": "string", + "x-stoplight": { + "id": "hfupdk750wcrj" + } + }, + "time_zone": { + "type": "string", + "description": "IANA tz", + "x-stoplight": { + "id": "izvtty345nfsz" + } + }, + "latitude": { + "type": "number", + "x-stoplight": { + "id": "9l62t4cwsp53w" + }, + "nullable": true + }, + "longitude": { + "type": "number", + "x-stoplight": { + "id": "3tzibc0li8xvg" + }, + "nullable": true + } + } + }, + "type": { + "type": "string", + "enum": [ + "metro_area" + ] + }, + "": { + "type": "string", + "x-stoplight": { + "id": "kwcjunrtu3r5o" + } + } + }, + "required": [ + "id", + "type" + ] + }, + "terminal": { + "title": "Terminal model", + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "relationships": { + "type": "object", + "required": [ + "port" + ], + "properties": { + "port": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "port" + ] + } + } + } + } + } + } + }, + "attributes": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + }, + "nickname": { + "type": "string" + }, + "firms_code": { + "type": "string", + "description": "CBP FIRMS Code or CBS Sublocation Code" + }, + "smdg_code": { + "type": "string", + "description": "SMDG Code" + }, + "bic_facility_code": { + "type": "string", + "description": "BIC Facility Code" + }, + "street": { + "type": "string", + "description": "Street part of the address" + }, + "city": { + "type": "string", + "description": "City part of the address" + }, + "state": { + "type": "string", + "description": "State part of the address" + }, + "state_abbr": { + "type": "string", + "description": "State abbreviation for the state" + }, + "zip": { + "type": "string", + "description": "ZIP code part of the address" + }, + "country": { + "type": "string", + "description": "Country part of the address" + } + } + }, + "type": { + "type": "string", + "enum": [ + "terminal" + ] + } + }, + "required": [ + "attributes", + "relationships" + ] + }, + "rail_terminal": { + "title": "Rail Terminal model", + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "relationships": { + "type": "object", + "properties": { + "port": { + "type": "object", + "properties": { + "data": { + "type": "object", + "nullable": true, + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "port" + ] + } + } + } + } + }, + "metro_area": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "metro_area" + ] + } + } + } + } + } + } + }, + "attributes": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + }, + "nickname": { + "type": "string" + }, + "firms_code": { + "type": "string", + "description": "CBP FIRMS Code or CBS Sublocation Code" + } + } + }, + "type": { + "type": "string", + "enum": [ + "rail_terminal" + ] + } + }, + "required": [ + "attributes" + ] + }, + "tracking_request": { + "title": "Tracking Request", + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "tracking_request" + ] + }, + "attributes": { + "type": "object", + "properties": { + "request_number": { + "type": "string", + "example": "ONEYSH9AME650500" + }, + "ref_numbers": { + "type": "array", + "nullable": true, + "items": { + "type": "string" + } + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + }, + "status": { + "type": "string", + "enum": [ + "pending", + "awaiting_manifest", + "created", + "failed", + "tracking_stopped" + ] + }, + "failed_reason": { + "type": "string", + "enum": [ + "booking_cancelled", + "duplicate", + "expired", + "internal_processing_error", + "invalid_number", + "not_found", + "retries_exhausted", + "shipping_line_unreachable", + "unrecognized_response", + "data_unavailable", + null + ], + "description": "If the tracking request has failed, or is currently failing, the last reason we were unable to complete the request", + "nullable": true + }, + "request_type": { + "type": "string", + "enum": [ + "bill_of_lading", + "booking_number", + "container" + ], + "example": "bill_of_lading" + }, + "scac": { + "type": "string", + "example": "ONEY", + "minLength": 4, + "maxLength": 4 + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "updated_at": { + "type": "string", + "format": "date-time" + }, + "is_retrying": { + "type": "boolean" + }, + "retry_count": { + "type": "integer", + "description": "How many times T49 has attempted to get the shipment from the shipping line", + "nullable": true + } + }, + "required": [ + "request_number", + "status", + "request_type", + "scac", + "created_at" + ] + }, + "relationships": { + "type": "object", + "properties": { + "tracked_object": { + "type": "object", + "properties": { + "data": { + "type": "object", + "nullable": true, + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "shipment" + ] + } + } + } + } + }, + "customer": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "party" + ] + } + } + } + } + } + } + } + }, + "required": [ + "id", + "type" + ] + }, + "webhook": { + "title": "webhook", + "type": "object", + "x-examples": {}, + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "webhook" + ] + }, + "attributes": { + "type": "object", + "properties": { + "url": { + "type": "string", + "format": "uri", + "description": "https end point" + }, + "active": { + "type": "boolean", + "default": true, + "description": "Whether the webhook will be delivered when events are triggered" + }, + "events": { + "type": "array", + "description": "The list of events to enabled for this endpoint", + "uniqueItems": true, + "minItems": 1, + "items": { + "type": "string", + "enum": [ + "container.transport.vessel_arrived", + "container.transport.vessel_discharged", + "container.transport.vessel_loaded", + "container.transport.vessel_departed", + "container.transport.rail_departed", + "container.transport.rail_arrived", + "container.transport.rail_loaded", + "container.transport.rail_unloaded", + "container.transport.transshipment_arrived", + "container.transport.transshipment_discharged", + "container.transport.transshipment_loaded", + "container.transport.transshipment_departed", + "container.transport.feeder_arrived", + "container.transport.feeder_discharged", + "container.transport.feeder_loaded", + "container.transport.feeder_departed", + "container.transport.empty_out", + "container.transport.full_in", + "container.transport.full_out", + "container.transport.empty_in", + "container.transport.vessel_berthed", + "shipment.estimated.arrival", + "tracking_request.succeeded", + "tracking_request.failed", + "tracking_request.awaiting_manifest", + "tracking_request.tracking_stopped", + "container.created", + "container.updated", + "container.pod_terminal_changed", + "container.transport.arrived_at_inland_destination", + "container.transport.estimated.arrived_at_inland_destination", + "container.pickup_lfd.changed" + ] + } + }, + "secret": { + "type": "string", + "description": "A random token that will sign all delivered webhooks" + }, + "headers": { + "type": "array", + "nullable": true, + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + } + } + } + } + }, + "required": [ + "url", + "active", + "events", + "secret" + ] + } + }, + "required": [ + "id", + "type" + ], + "description": "" + }, + "vessel": { + "title": "vessel", + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "vessel" + ] + }, + "attributes": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of the ship or vessel", + "example": "Ever Given" + }, + "imo": { + "type": "string", + "description": "International Maritime Organization (IMO) number", + "nullable": true, + "example": "9811000" + }, + "mmsi": { + "type": "string", + "description": "Maritime Mobile Service Identity (MMSI)", + "nullable": true, + "example": "353136000" + }, + "latitude": { + "type": "number", + "description": "The current latitude position of the vessel", + "nullable": true, + "example": 25.29845 + }, + "longitude": { + "type": "number", + "description": "The current longitude position of the vessel", + "nullable": true, + "example": 121.217 + }, + "nautical_speed_knots": { + "type": "number", + "description": "The current speed of the ship in knots (nautical miles per hour)", + "nullable": true, + "example": 90 + }, + "navigational_heading_degrees": { + "type": "number", + "description": "The current heading of the ship in degrees, where 0 is North, 90 is East, 180 is South, and 270 is West", + "nullable": true, + "example": 194 + }, + "position_timestamp": { + "type": "string", + "description": "The timestamp of when the ship's position was last recorded, in ISO 8601 date and time format", + "nullable": true, + "example": "2023-07-28T14:01:37Z" + } + } + } + } + }, + "transport_event": { + "title": "Transport Event Model", + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "transport_event" + ] + }, + "attributes": { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": [ + "container.transport.vessel_arrived", + "container.transport.vessel_discharged", + "container.transport.vessel_loaded", + "container.transport.vessel_departed", + "container.transport.rail_departed", + "container.transport.rail_arrived", + "container.transport.rail_loaded", + "container.transport.rail_unloaded", + "container.transport.transshipment_arrived", + "container.transport.transshipment_discharged", + "container.transport.transshipment_loaded", + "container.transport.transshipment_departed", + "container.transport.feeder_arrived", + "container.transport.feeder_discharged", + "container.transport.feeder_loaded", + "container.transport.feeder_departed", + "container.transport.empty_out", + "container.transport.full_in", + "container.transport.full_out", + "container.transport.empty_in", + "container.transport.vessel_berthed", + "container.transport.arrived_at_inland_destination", + "container.transport.estimated.arrived_at_inland_destination", + "container.pickup_lfd.changed" + ] + }, + "voyage_number": { + "type": "string", + "nullable": true + }, + "timestamp": { + "type": "string", + "format": "date-time", + "nullable": true + }, + "timezone": { + "type": "string", + "description": "IANA tz", + "nullable": true + }, + "location_locode": { + "type": "string", + "description": "UNLOCODE of the event location", + "nullable": true + }, + "created_at": { + "type": "string", + "format": "date-time" + }, + "data_source": { + "type": "string", + "enum": [ + "shipping_line", + "terminal", + "ais" + ], + "example": "shipping_line", + "description": "The original source of the event data" + } + } + }, + "relationships": { + "type": "object", + "properties": { + "shipment": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "shipment" + ] + } + } + } + } + }, + "location": { + "type": "object", + "properties": { + "data": { + "type": "object", + "nullable": true, + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "port", + "metro_area" + ] + } + } + } + } + }, + "vessel": { + "type": "object", + "properties": { + "data": { + "type": "object", + "nullable": true, + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "name": { + "type": "string", + "enum": [ + "vessel" + ] + } + } + } + } + }, + "terminal": { + "type": "object", + "properties": { + "data": { + "type": "object", + "nullable": true, + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "terminal", + "rail_terminal" + ] + } + } + } + } + }, + "container": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "container" + ] + } + } + } + } + } + } + } + }, + "required": [ + "id", + "type" + ] + }, + "estimated_event": { + "title": "Estimated Event Model", + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "estimated_event" + ] + }, + "attributes": { + "type": "object", + "required": [ + "created_at", + "estimated_timestamp", + "event" + ], + "properties": { + "created_at": { + "type": "string", + "description": "When the estimated event was created", + "format": "date-time" + }, + "estimated_timestamp": { + "type": "string", + "format": "date-time" + }, + "event": { + "type": "string", + "enum": [ + "shipment.estimated.arrival" + ] + }, + "location_locode": { + "type": "string", + "description": "UNLOCODE of the event location", + "nullable": true + }, + "timezone": { + "type": "string", + "description": "IANA tz", + "nullable": true + }, + "voyage_number": { + "type": "string", + "nullable": true + }, + "data_source": { + "type": "string", + "enum": [ + "shipping_line", + "terminal" + ], + "description": "The original source of the event data" + } + } + }, + "relationships": { + "type": "object", + "required": [ + "shipment" + ], + "properties": { + "shipment": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "type": "object", + "required": [ + "id", + "type" + ], + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "shipment" + ] + } + } + } + } + }, + "port": { + "type": "object", + "properties": { + "data": { + "type": "object", + "nullable": true, + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "port" + ] + } + } + } + } + }, + "vessel": { + "type": "object", + "description": "\n", + "properties": { + "data": { + "type": "object", + "nullable": true, + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "vessel" + ] + } + } + } + } + } + } + } + }, + "required": [ + "id", + "type", + "attributes", + "relationships" + ] + }, + "webhook_notification": { + "title": "webhook_notification", + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "webhook_notification" + ] + }, + "attributes": { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": [ + "container.transport.vessel_arrived", + "container.transport.vessel_discharged", + "container.transport.vessel_loaded", + "container.transport.vessel_departed", + "container.transport.rail_departed", + "container.transport.rail_arrived", + "container.transport.rail_loaded", + "container.transport.rail_unloaded", + "container.transport.transshipment_arrived", + "container.transport.transshipment_discharged", + "container.transport.transshipment_loaded", + "container.transport.transshipment_departed", + "container.transport.feeder_arrived", + "container.transport.feeder_discharged", + "container.transport.feeder_loaded", + "container.transport.feeder_departed", + "container.transport.empty_out", + "container.transport.full_in", + "container.transport.full_out", + "container.transport.empty_in", + "container.transport.vessel_berthed", + "shipment.estimated.arrival", + "tracking_request.succeeded", + "tracking_request.failed", + "tracking_request.awaiting_manifest", + "tracking_request.tracking_stopped", + "container.created", + "container.updated", + "container.pod_terminal_changed", + "container.transport.arrived_at_inland_destination", + "container.transport.estimated.arrived_at_inland_destination", + "container.pickup_lfd.changed" + ] + }, + "delivery_status": { + "type": "string", + "default": "pending", + "enum": [ + "pending", + "succeeded", + "failed" + ], + "description": "Whether the notification has been delivered to the webhook endpoint" + }, + "created_at": { + "type": "string" + } + }, + "required": [ + "event", + "delivery_status", + "created_at" + ] + }, + "relationships": { + "type": "object", + "properties": { + "webhook": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "webhook" + ] + } + } + } + } + }, + "reference_object": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "tracking_request", + "estimated_event", + "transport_event", + "container_updated_event" + ] + } + } + } + } + } + }, + "required": [ + "webhook" + ] + } + } + }, + "terminal_hold": { + "title": "terminal_hold", + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "status": { + "type": "string", + "enum": [ + "pending", + "hold" + ] + }, + "description": { + "type": "string", + "description": "Text description from the terminal (if any)", + "nullable": true + } + }, + "required": [ + "name", + "status" + ] + }, + "terminal_fee": { + "title": "terminal_fee", + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": [ + "demurrage", + "exam", + "extended_dwell_time", + "other", + "total" + ] + }, + "amount": { + "type": "number", + "description": "The fee amount in local currency" + }, + "currency_code": { + "type": "string", + "description": "The ISO 4217 currency code of the fee is charged in. E.g. USD", + "example": "USD" + } + }, + "required": [ + "type", + "amount" + ] + }, + "container_updated_event": { + "title": "container_updated_event", + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string" + }, + "attributes": { + "type": "object", + "properties": { + "changeset": { + "type": "object", + "description": "A hash of all the changed attributes with the values being an array of the before and after. E.g. \n`{\"pickup_lfd\": [null, \"2020-05-20\"]}`\n\nThe current attributes that can be alerted on are:\n- `available_for_pickup`\n- `pickup_lfd`\n- `fees_at_pod_terminal`\n- `holds_at_pod_terminal`\n- `pickup_appointment_at`\n- `pod_terminal`" + }, + "timestamp": { + "type": "string", + "format": "date-time", + "description": "" + }, + "timezone": { + "type": "string", + "description": "IANA tz " + }, + "data_source": { + "type": "string", + "enum": [ + "terminal" + ], + "example": "terminal" + } + }, + "required": [ + "changeset", + "timestamp" + ] + }, + "relationships": { + "type": "object", + "required": [ + "container", + "terminal" + ], + "properties": { + "container": { + "type": "object", + "required": [ + "data" + ], + "properties": { + "data": { + "type": "object", + "required": [ + "id", + "type" + ], + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "container" + ] + } + } + } + } + }, + "terminal": { + "type": "object", + "description": "", + "required": [ + "data" + ], + "properties": { + "data": { + "type": "object", + "required": [ + "id", + "type" + ], + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "terminal" + ] + } + } + } + } + } + } + } + }, + "required": [ + "relationships" + ] + }, + "raw_event": { + "title": "Raw Event Model", + "type": "object", + "description": "Raw Events represent the milestones from the shipping line for a given container.\n\n### About raw_event datetimes\n\nThe events may include estimated future events. The event is a future event if an `estimated_` timestamp is not null. \n\nThe datetime properties `timestamp` and `estimated`. \n\nWhen the `time_zone` property is present the datetimes are UTC timestamps, which can be converted to the local time by parsing the provided `time_zone`.\n\nWhen the `time_zone` property is absent, the datetimes represent local times which serialized as UTC timestamps for consistency. ", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string", + "description": "", + "enum": [ + "raw_event" + ] + }, + "attributes": { + "type": "object", + "properties": { + "event": { + "type": "string", + "enum": [ + "empty_out", + "full_in", + "positioned_in", + "positioned_out", + "vessel_loaded", + "vessel_departed", + "transshipment_arrived", + "transshipment_discharged", + "transshipment_loaded", + "transshipment_departed", + "feeder_arrived", + "feeder_discharged", + "feeder_loaded", + "feeder_departed", + "rail_loaded", + "rail_departed", + "rail_arrived", + "rail_unloaded", + "vessel_arrived", + "vessel_discharged", + "arrived_at_destination", + "delivered", + "full_out", + "empty_in", + "vgm_received", + "carrier_release", + "customs_release" + ], + "description": "Normalized string representing the event", + "nullable": true + }, + "original_event": { + "type": "string", + "description": "The event name as returned by the carrier" + }, + "timestamp": { + "type": "string", + "format": "date-time", + "description": "The datetime the event either transpired or will occur in UTC" + }, + "estimated": { + "type": "boolean", + "description": "True if the timestamp is estimated, false otherwise" + }, + "actual_on": { + "type": "string", + "format": "date", + "description": "Deprecated: The date of the event at the event location when no time information is available. ", + "nullable": true + }, + "estimated_on": { + "type": "string", + "format": "date", + "description": "Deprecated: The estimated date of the event at the event location when no time information is available. ", + "nullable": true + }, + "actual_at": { + "type": "string", + "format": "date-time", + "description": "Deprecated: The datetime the event transpired in UTC", + "nullable": true + }, + "estimated_at": { + "type": "string", + "format": "date-time", + "description": "Deprecated: The estimated datetime the event will occur in UTC", + "nullable": true + }, + "timezone": { + "type": "string", + "description": "IANA tz where the event occured", + "nullable": true + }, + "created_at": { + "type": "string", + "format": "date-time", + "description": "When the raw_event was created in UTC" + }, + "location_name": { + "type": "string", + "description": "The city or facility name of the event location" + }, + "location_locode": { + "type": "string", + "description": "UNLOCODE of the event location", + "nullable": true + }, + "vessel_name": { + "type": "string", + "description": "The name of the vessel where applicable", + "nullable": true + }, + "vessel_imo": { + "type": "string", + "description": "The IMO of the vessel where applicable", + "nullable": true + }, + "index": { + "type": "integer", + "description": "The order of the event. This may be helpful when only dates (i.e. actual_on) are available." + }, + "voyage_number": { + "type": "string", + "nullable": true + } + } + }, + "relationships": { + "type": "object", + "properties": { + "location": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "port", + "metro_area" + ] + } + } + } + } + }, + "vessel": { + "type": "object", + "properties": { + "data": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "vessel" + ] + } + } + } + } + } + } + } + } + }, + "container_pod_terminal_changed_event": { + "title": "Container Pod Terminal Changed Event", + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string" + }, + "attributes": { + "type": "object", + "properties": { + "data_source": { + "type": "string", + "enum": [ + "shipping_line", + "terminal", + "pierpass" + ], + "example": "shipping_line", + "description": "Where the information about the terminal change came from" + }, + "timestamp": { + "type": "string", + "format": "date-time", + "description": "When the terminal change was recorded" + } + } + }, + "relationships": { + "type": "object", + "properties": { + "container": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "container" + ] + } + } + }, + "shipment": { + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "shipment" + ] + } + } + }, + "terminal": { + "type": "object", + "description": "The terminal the container has changed to. If this container is still on the vessel this represents an advisory. If it was previously at the terminal this represents an off-dock move.", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "type": { + "type": "string", + "enum": [ + "terminal" + ] + } + } + } + } + } + }, + "description": "" + }, + "party": { + "title": "Party model", + "type": "object", + "properties": { + "id": { + "type": "string", + "format": "uuid" + }, + "attributes": { + "type": "object", + "required": [ + "company_name" + ], + "properties": { + "company_name": { + "type": "string", + "description": "Company name" + } + } + }, + "type": { + "type": "string", + "enum": [ + "party" + ] + } + }, + "required": [ + "attributes" + ] + } + }, + "securitySchemes": { + "authorization": { + "name": "Authorization", + "type": "apiKey", + "in": "header", + "description": "`Token YOUR_API_TOKEN`\n\nThe APIs require authentication to be done using header-based API Key and Secret Authentication. \n\nAPI key and secret are sent va the `Authorization` request header.\n\nYou send your API key and secret in the following way:\n\n`Authorization: Token YOUR_API_KEY`" + } + } + }, + "tags": [ + { + "name": "Containers" + }, + { + "name": "Shipments" + }, + { + "name": "Locations" + }, + { + "name": "Events" + }, + { + "name": "Tracking Requests" + }, + { + "name": "Webhooks" + }, + { + "name": "Webhook Notifications" + }, + { + "name": "Ports" + }, + { + "name": "Metro Areas" + }, + { + "name": "Terminals" + } + ], + "security": [ + { + "authorization": [] + } + ] +} \ No newline at end of file diff --git a/endpoint-documentation-template.md b/endpoint-documentation-template.md new file mode 100644 index 00000000..a2fbd5a5 --- /dev/null +++ b/endpoint-documentation-template.md @@ -0,0 +1,102 @@ +# Endpoint Documentation Template + +## Overview +This template provides a standardized format for documenting Terminal49 API endpoints with comprehensive examples, clear explanations, and business context. + +--- + +```yaml +--- +title: [Endpoint Name - e.g., "Create a Tracking Request"] +description: [Brief description of what this endpoint does] +openapi: [method] [path] # e.g., post /tracking_requests +--- +``` + +## Endpoint Purpose +[1-2 sentences explaining what this endpoint is used for and when you would use it in your integration] + +## Business Use Cases +[Brief explanation of common business scenarios where this endpoint is valuable] +- **Use Case 1**: [e.g., "Initiate tracking for a new container shipment"] +- **Use Case 2**: [e.g., "Set up automated monitoring for a customer's bill of lading"] + +## Request Format + +### Required Parameters +[List and explain required parameters] + +### Optional Parameters +[List and explain optional parameters] + +### Example Requests + +#### Example 1: [Basic/Common Scenario] +```json +// Request +{ + [Request body with comments explaining key fields] +} + +// Response +{ + [Success response with comments explaining important fields] +} +``` + +#### Example 2: [Alternative Scenario] +```json +// Request +{ + [Request body for alternative scenario] +} + +// Response +{ + [Response showing different outcome] +} +``` + +#### Example 3: [Error Scenario] +```json +// Request +{ + [Request body that would cause an error] +} + +// Response +{ + [Error response with explanation] +} +``` + +## Response Fields +| Field | Type | Description | +|-------|------|-------------| +| `field1` | string | [Explanation with business context] | +| `field2` | integer | [Explanation with business context] | + +## Status Codes +| Code | Meaning | +|------|---------| +| 200 | Success - [what this means] | +| 400 | Bad Request - [common causes] | +| 401 | Unauthorized - [authentication issue] | +| 422 | Unprocessable Entity - [validation errors] | + +## Related Webhooks +If you've registered for webhooks, you may receive the following events related to this endpoint: +- `event.name`: [Description of when this event is triggered] +- `event.name2`: [Description of when this event is triggered] + +## Related Guides +- [Link to related guide] +- [Link to tutorial that uses this endpoint] + +## Troubleshooting +Common issues and their solutions: +- **Issue**: [Common error or confusion] + **Solution**: [How to resolve it] + +- **Issue**: [Another common issue] + **Solution**: [How to resolve it] diff --git a/fix_json.py b/fix_json.py new file mode 100644 index 00000000..fdcd9f2e --- /dev/null +++ b/fix_json.py @@ -0,0 +1,44 @@ +import json +import sys + +try: + with open('docs/openapi.json', 'r') as f: + content = f.read() + + # Try to load the JSON to check if it's valid + try: + data = json.loads(content) + print("JSON is valid!") + except json.JSONDecodeError as e: + print(f"JSON error: {e}") + + # Examine the problematic area + error_pos = e.pos + start = max(0, error_pos - 20) + end = min(len(content), error_pos + 20) + problematic_content = content[start:end] + + print(f"Problematic content around position {error_pos}:") + print(repr(problematic_content)) + + # Fix the content by ensuring proper JSON format (remove any trailing characters) + # Find the last valid closing brace + last_brace_pos = content.rstrip().rfind('}') + if last_brace_pos > 0: + fixed_content = content[:last_brace_pos+1] + + # Verify the fixed content + try: + json.loads(fixed_content) + print("Fixed JSON is valid!") + + # Save the fixed content back to the file + with open('docs/openapi.json', 'w') as f: + f.write(fixed_content) + print("File has been fixed and saved.") + except json.JSONDecodeError as e2: + print(f"Could not fix JSON: {e2}") + else: + print("Could not find closing brace to fix JSON.") +except Exception as e: + print(f"Error: {e}") diff --git a/fix_json_thorough.py b/fix_json_thorough.py new file mode 100644 index 00000000..90d5f3c8 --- /dev/null +++ b/fix_json_thorough.py @@ -0,0 +1,46 @@ +import json +import re + +# Attempt to clean and parse the JSON file +try: + with open('docs/openapi.json', 'r') as f: + content = f.read() + + # Look for the main JSON structure + # Find where the root object ends (the last closing brace that matches the first opening brace) + open_braces = 0 + clean_end_pos = None + + for i, char in enumerate(content): + if char == '{': + open_braces += 1 + elif char == '}': + open_braces -= 1 + if open_braces == 0: + clean_end_pos = i + 1 + break + + if clean_end_pos is not None: + # Extract just the clean JSON part + clean_content = content[:clean_end_pos] + + # Make a backup + with open('docs/openapi.json.backup', 'w') as f: + f.write(content) + + # Validate the cleaned content + try: + parsed = json.loads(clean_content) + print("Successfully cleaned and parsed the JSON!") + + # Write the clean, formatted JSON back to the file + with open('docs/openapi.json', 'w') as f: + json.dump(parsed, f, indent=2) + + print("Fixed JSON has been written back to the file.") + except json.JSONDecodeError as e: + print(f"Still issues with the JSON: {e}") + else: + print("Could not find the end of the root JSON object.") +except Exception as e: + print(f"Error: {e}") diff --git a/fix_openapi.py b/fix_openapi.py new file mode 100644 index 00000000..5d141b03 --- /dev/null +++ b/fix_openapi.py @@ -0,0 +1,35 @@ +import json +import os + +# Open the file and read line by line to understand the issue +with open('docs/openapi.json', 'r') as f: + lines = f.readlines() + +# Find where the main JSON object ends +end_line = None +for i, line in enumerate(lines): + if line.strip() == '}' and i > len(lines) - 5: # Look for closing brace near the end + end_line = i + break + +if end_line is not None: + # Keep only up to the end of the JSON object + fixed_lines = lines[:end_line+1] + fixed_content = ''.join(fixed_lines) + + # Validate the fixed content + try: + json.loads(fixed_content) + print("Fixed JSON is valid!") + + # Create a backup + os.rename('docs/openapi.json', 'docs/openapi.json.bak') + + # Save the fixed content + with open('docs/openapi.json', 'w') as f: + f.write(fixed_content) + print("File has been fixed and saved. Original backed up to openapi.json.bak.") + except json.JSONDecodeError as e: + print(f"Could not fix JSON: {e}") +else: + print("Could not identify where to fix the JSON.") diff --git a/json-api-guide.mdx b/json-api-guide.mdx new file mode 100644 index 00000000..80bbefa8 --- /dev/null +++ b/json-api-guide.mdx @@ -0,0 +1,255 @@ +--- +title: Understanding JSON:API +description: Learn the basics of JSON:API and how it's implemented in Terminal49's API. +--- + +# JSON:API Guide + +Terminal49's API follows the [JSON:API specification](https://jsonapi.org/), a standard for building APIs in JSON. This guide will help you understand the key concepts and how to work with our API effectively. + +## What is JSON:API? + +JSON:API is a specification for how a client should request resources to be fetched or modified, and how a server should respond. It's designed to minimize both the number of requests and the amount of data transmitted between clients and servers. + + + The Terminal49 API conforms to the JSON:API v1.0 specification. This standardization makes our API more predictable and easier to work with once you understand the patterns. + + +## JSON:API Structure + +### Basic Resource Objects + +All Terminal49 API responses follow this general structure: + +```json +{ + "data": { + "id": "123", + "type": "shipment", + "attributes": { + "bill_of_lading_number": "MAEU9736478", + "created_at": "2023-05-15T14:23:12Z" + // Other shipment attributes + }, + "relationships": { + "containers": { + "data": [ + { "id": "456", "type": "container" }, + { "id": "789", "type": "container" } + ] + } + } + } +} +``` + +The main components are: + + + The primary resource or collection of resources. + + + + A unique identifier for the resource. + + + + The resource type (e.g., "shipment", "container", "tracking_request"). + + + + The resource's properties (e.g., numbers, dates, status values). + + + + References to related resources (e.g., a shipment's containers). + + +### Collections of Resources + +When requesting multiple resources, the response structure is: + +```json +{ + "data": [ + { + "id": "123", + "type": "shipment", + "attributes": { ... } + }, + { + "id": "124", + "type": "shipment", + "attributes": { ... } + } + ], + "meta": { + "total_count": 45 + }, + "links": { + "self": "https://api.terminal49.com/v2/shipments?page[number]=1&page[size]=2", + "next": "https://api.terminal49.com/v2/shipments?page[number]=2&page[size]=2" + } +} +``` + + + Contains non-standard meta-information, like counts or pagination details. + + + + Contains links for pagination or related operations. + + +## Working with JSON:API in Terminal49 + +### Creating Resources + +When creating a resource (like a tracking request), your request should follow this format: + + + + { + "data": { + "type": "tracking_request", + "attributes": { + "request_type": "bill_of_lading", + "request_number": "MAEU9736478", + "scac": "MAEU" + } + } + } + + + +Notice that: +- The `type` field is required and must match the expected resource type +- All properties go inside the `attributes` object +- You don't specify an `id` for new resources (the server assigns it) + +### Including Related Resources + +One powerful feature of JSON:API is the ability to include related resources in a single request using the `include` parameter: + +``` +GET /shipments/123?include=containers,containers.transport_events +``` + +This returns the shipment with its containers and each container's transport events in one response: + +```json +{ + "data": { + "id": "123", + "type": "shipment", + "attributes": { ... }, + "relationships": { + "containers": { + "data": [ + { "id": "456", "type": "container" } + ] + } + } + }, + "included": [ + { + "id": "456", + "type": "container", + "attributes": { ... }, + "relationships": { + "transport_events": { + "data": [ + { "id": "789", "type": "transport_event" } + ] + } + } + }, + { + "id": "789", + "type": "transport_event", + "attributes": { ... } + } + ] +} +``` + + + Contains all the related resources referenced in the main resource's relationships. + + +### Error Handling + +Error responses also follow a standard format: + +```json +{ + "errors": [ + { + "status": "422", + "title": "Invalid attribute", + "detail": "The SCAC 'UNKNOWN' is not supported.", + "source": { + "pointer": "/data/attributes/scac" + } + } + ] +} +``` + +Each error object contains: + + + The HTTP status code relevant to the error. + + + + A short, human-readable summary of the problem. + + + + A human-readable explanation of the specific error. + + + + An object containing references to the source of the error. The `pointer` field typically contains a JSON Pointer to the specific field that caused the error. + + +## JSON:API Libraries + +Since Terminal49 uses standard JSON:API, you can leverage client libraries for your preferred programming language to make working with our API easier: + + + + JSON:API libraries for JavaScript + + + jsonapi-rb for Ruby applications + + + JSON:API libraries for Python + + + +Using a library helps handle the resource structure, relationship management, and error parsing automatically. + +## Tips for Working with Terminal49's JSON:API + +- Always include the correct `Content-Type` header in your requests: + ``` + Content-Type: application/vnd.api+json + ``` + +- Use the `include` parameter to reduce the number of API calls when you need related resources + +- When creating or updating resources, remember that all properties must be inside the `attributes` object + +- For filtering collections, use the filter query parameter with the attribute name: + ``` + GET /containers?filter[status]=arrived + ``` + +- Pagination is handled via `page[number]` and `page[size]` query parameters + + + For a deeper understanding of JSON:API, check the [official specification](https://jsonapi.org/format/) or our [In-Depth Guides](/api-docs/in-depth-guides/including-resources) on working with Terminal49's API. + diff --git a/mintlify-endpoint-documentation-template.mdx b/mintlify-endpoint-documentation-template.mdx new file mode 100644 index 00000000..d897b532 --- /dev/null +++ b/mintlify-endpoint-documentation-template.mdx @@ -0,0 +1,151 @@ +--- +title: [Endpoint Name - e.g., "Create a Tracking Request"] +description: [Brief description of what this endpoint does] +og:title: [Endpoint Name] | Terminal49 API Documentation +og:description: [SEO-friendly description about this endpoint] +openapi: [method] [path] # e.g., post /tracking_requests +--- + +## Endpoint Purpose +[1-2 sentences explaining what this endpoint is used for and when you would use it in your integration] + +## Business Use Cases +[Brief explanation of common business scenarios where this endpoint is valuable] +- **Use Case 1**: [e.g., "Initiate tracking for a new container shipment"] +- **Use Case 2**: [e.g., "Set up automated monitoring for a customer's bill of lading"] + +## Example Scenarios + + + + + + { + // Request body with comments + } + + + { + // Success response with comments + } + + + + **Notes:** + - [Important information about this scenario] + - [Explanation of key response fields] + + + + + + { + // Request body for alternative scenario + } + + + { + // Response showing different outcome + } + + + + **Notes:** + - [Information specific to this scenario] + - [Business context for when you'd use this approach] + + + + + + { + // Request that would trigger an error + } + + + { + // Error response + } + + + + **Notes:** + - [Explanation of what caused the error] + - [How to resolve this issue] + + + +## Key Response Fields + + + [Explanation with business context] + + + + [Explanation with business context] + + + + An object containing related information. + + + + [Explanation of nested field] + + + + [Explanation of nested array field] + + + + +## Related Webhook Events + +When using this endpoint, you may receive the following webhook events: + + + [Description of when this event is triggered and what it contains] + + + + [Description of when this event is triggered and what it contains] + + +## Related Guides & Tutorials + + + + [Brief description of related guide] + + + [Brief description of related tutorial] + + + +## Troubleshooting + + + + **Problem**: [Description of common problem] + + **Solution**: [Detailed steps to resolve] + + ```json + // Example of correct usage + ``` + + + + **Problem**: [Description of another common problem] + + **Solution**: [Detailed steps to resolve] + + + + + [Important information or tips about using this endpoint effectively] + + + + [Critical warnings about potential pitfalls or limitations] + diff --git a/sample-create-tracking-request.mdx b/sample-create-tracking-request.mdx new file mode 100644 index 00000000..0b668eda --- /dev/null +++ b/sample-create-tracking-request.mdx @@ -0,0 +1,308 @@ +--- +title: Create a Tracking Request +description: Submit a new tracking request to monitor ocean shipments via bill of lading, booking number, or container number. +og:title: Create a Tracking Request | Terminal49 API Documentation +og:description: Create tracking requests via Terminal49's API for accurate shipment tracking and real-time notifications. +openapi: post /tracking_requests +--- + +## Endpoint Purpose +Submitting a tracking request tells Terminal49 to start monitoring a specific shipment for you. This is typically the first step in the tracking workflow. + +## Business Use Cases +- **Initial Tracking Setup**: Start tracking a new container shipment when you receive a bill of lading or booking confirmation. +- **Customer Onboarding**: Quickly set up tracking for a customer's shipments when they provide their shipping documents. +- **Bulk Import**: Programmatically submit multiple tracking requests to monitor your entire inventory in transit. + +## Example Scenarios + + + + + + { + "data": { + "type": "tracking_request", + "attributes": { + "request_type": "bill_of_lading", // Type of tracking number + "request_number": "MAEU9736478", // Bill of lading number from the carrier + "scac": "MAEU" // Shipping line SCAC code (Maersk in this case) + } + } + } + + + { + "data": { + "id": "b9c6a3e0-411e-4378-b93c-aacea42a673e", + "type": "tracking_request", + "attributes": { + "request_type": "bill_of_lading", + "request_number": "MAEU9736478", + "scac": "MAEU", + "status": "pending", // Request is being processed + "failed_reason": null, + "created_at": "2023-05-15T14:23:12Z", + "updated_at": "2023-05-15T14:23:12Z" + }, + "relationships": { + "shipment": { + "data": null // Will be populated when tracking succeeds + } + } + } + } + + + + **Notes:** + - The bill of lading is typically the most reliable identifier for tracking shipments. + - The `status` will initially be `pending` while Terminal49 processes your request. + - Once processing completes, you'll receive a webhook notification and the status will change to `succeeded` or `failed`. + + + + + + { + "data": { + "type": "tracking_request", + "attributes": { + "request_type": "container", // Tracking by container number + "request_number": "MSCU1234567", // Container number + "scac": "MSCU" // Shipping line SCAC code (MSC in this case) + } + } + } + + + { + "data": { + "id": "f7c9b5d2-311e-4378-a91c-bbcea42a785f", + "type": "tracking_request", + "attributes": { + "request_type": "container", + "request_number": "MSCU1234567", + "scac": "MSCU", + "status": "pending", + "failed_reason": null, + "created_at": "2023-05-15T15:45:22Z", + "updated_at": "2023-05-15T15:45:22Z" + }, + "relationships": { + "shipment": { + "data": null + } + } + } + } + + + + **Notes:** + - Container number tracking support varies between carriers. Refer to the Carrier Data Matrix to verify support. + - Container numbers should include the carrier prefix (e.g., MSCU for MSC containers). + - Container tracking can sometimes fail if the container is very new or not yet associated with a booking in the carrier's system. + + + + + + { + "data": { + "type": "tracking_request", + "attributes": { + "request_type": "bill_of_lading", + "request_number": "TEST-TR-SUCCEEDED", // Test number that will always succeed + "scac": "TEST" // Special SCAC for test requests + } + } + } + + + { + "data": { + "id": "e8d7c6b5-211d-4378-a81b-aacfa42a895e", + "type": "tracking_request", + "attributes": { + "request_type": "bill_of_lading", + "request_number": "TEST-TR-SUCCEEDED", + "scac": "TEST", + "status": "pending", + "failed_reason": null, + "created_at": "2023-05-15T16:30:45Z", + "updated_at": "2023-05-15T16:30:45Z" + }, + "relationships": { + "shipment": { + "data": null + } + } + } + } + + + + **Notes:** + - Using test numbers is perfect for development and testing your integration. + - `TEST-TR-SUCCEEDED` will always trigger a successful tracking response. + - `TEST-TR-FAILED` will always trigger a failed tracking response. + - These test numbers will trigger corresponding webhook events for complete testing. + + + + + + { + "data": { + "type": "tracking_request", + "attributes": { + "request_type": "bill_of_lading", + "request_number": "INVALID-FORMAT", // Invalid bill of lading format + "scac": "UNKNOWN" // Non-existent SCAC code + } + } + } + + + { + "errors": [ + { + "status": "422", + "title": "Invalid SCAC", + "detail": "The SCAC 'UNKNOWN' is not supported.", + "source": { + "pointer": "/data/attributes/scac" + } + } + ] + } + + + + **Notes:** + - This example shows a validation error for an invalid SCAC code. + - Common errors include invalid SCAC codes, improperly formatted request numbers, or duplicate tracking requests. + - For a complete list of supported SCACs, refer to the Carrier Data Matrix. + + + +## Key Response Fields + + + Unique identifier for the tracking request. Store this to check status later. + + + + Current status of the tracking request. Possible values: + - `pending`: Request is being processed + - `succeeded`: Container/shipment located and tracking established + - `failed`: Unable to track the shipment (see failed_reason) + - `awaiting_manifest`: Bill of lading is valid but not yet available in the carrier's system + - `tracking_stopped`: Tracking has been stopped for this request + + + + If status is "failed", this field provides the reason. Common values: + - `duplicate`: Shipment already exists in Terminal49 + - `not_found`: Shipping line couldn't find the tracking number + - `retries_exhausted`: Maximum retries reached without success + - `invalid_number`: Improperly formatted tracking number + + + See the [Tracking Request Lifecycle](/api-docs/in-depth-guides/tracking-request-lifecycle) guide for a complete list of failure reasons. + + + + + When the tracking request succeeds, this contains a reference to the created shipment. + + + + ID of the linked shipment that you can use to retrieve full shipment details. + + + + Always "shipment" for successful tracking requests. + + + + +## Related Webhook Events + +When creating a tracking request, you may receive the following webhook events: + + + Sent when Terminal49 successfully locates your shipment and begins tracking it. The notification includes details about the created shipment and its containers. + + + + Sent when Terminal49 is unable to locate your shipment after all retry attempts. Includes the failed_reason to help troubleshoot. + + + + Sent when the bill of lading number is valid but the shipment data is not yet available in the carrier's system. Terminal49 will continue checking daily. + + +## Related Guides & Tutorials + + + + Learn about the full lifecycle of a tracking request, including all possible statuses and failure reasons. + + + Learn how to set up webhooks to receive real-time notifications about your tracking requests. + + + Complete list of test tracking numbers you can use during development. + + + Learn how to efficiently submit and manage multiple tracking requests. + + + +## Troubleshooting + + + + **Problem**: Your tracking request has been in "pending" status for an extended period. + + **Solution**: + 1. Terminal49 retries tracking requests up to 14 times over 24 hours. + 2. Check that the SCAC and request number are correct. + 3. For bill of lading numbers, note that they may remain in "awaiting_manifest" status for up to 7 days. + 4. If the issue persists beyond 24 hours, contact support@terminal49.com. + + + + **Problem**: Your tracking request failed with reason "not_found". + + **Solution**: + 1. Verify the tracking number format is correct (no spaces, proper prefixes). + 2. Confirm you're using the correct SCAC code for the carrier. + 3. For newly issued bills of lading, try again in 24-48 hours as it may take time to appear in the carrier's system. + 4. If using a container number, check if the carrier supports container tracking in the Carrier Data Matrix. + + + + **Problem**: Your tracking request failed with reason "duplicate". + + **Solution**: + 1. The shipment is already being tracked in Terminal49. + 2. Use `GET /shipments` with a filter to find the existing shipment: + + ```bash + GET /shipments?filter[bill_of_lading_number]=MAEU9736478 + ``` + + 3. You can access the existing shipment data instead of creating a new tracking request. + + + + + For the fastest tracking setup, use the master bill of lading (MBL) number from the carrier. House bills of lading (HBL) are not supported. + + + + Container number tracking is not supported by all carriers. Always check the Carrier Data Matrix before implementing container tracking. + diff --git a/sample.json b/sample.json new file mode 100644 index 00000000..7919c324 --- /dev/null +++ b/sample.json @@ -0,0 +1,100 @@ +{ + "openapi": "3.0.0", + "info": { + "title": "Terminal49 API Reference", + "version": "0.2.0", + "contact": { + "name": "Terminal49 API support", + "url": "https://www.terminal49.com", + "email": "support@terminal49.com" + }, + "description": "The Terminal 49 API offers a convenient way to programmatically track your shipments from origin to destination.", + "x-label": "Beta", + "termsOfService": "https://www.terminal49.com/terms" + }, + "servers": [ + { + "url": "https://api.terminal49.com/v2", + "description": "Production" + } + ], + "paths": { + "/shipments": { + "get": { + "summary": "List shipments", + "tags": [ + "Shipments" + ], + "responses": { + "200": { + "description": "OK", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/components/schemas/shipment" + } + }, + "included": { + "type": "array", + "items": { + "anyOf": [ + { + "$ref": "#/components/schemas/container" + }, + { + "$ref": "#/components/schemas/port" + }, + { + "$ref": "#/components/schemas/terminal" + } + ] + } + }, + "links": { + "$ref": "#/components/schemas/links" + }, + "meta": { + "$ref": "#/components/schemas/meta" + } + } + }, + "examples": { + "En-route to NY with inland move": { + "value": { + "data": [ + { + "id": "62738624-7032-4a50-892e-c55826228c25", + "type": "shipment", + "attributes": { + "created_at": "2024-06-26T17:28:59Z", + "ref_numbers": [], + "tags": [], + "bill_of_lading_number": "OOLU2148468620", + "normalized_number": "2148468620", + "shipping_line_scac": "OOLU", + "shipping_line_name": "Orient Overseas Container Line", + "shipping_line_short_name": "OOCL", + "customer_name": "Sodor Steamworks", + "port_of_lading_locode": "CNNBG", + "port_of_lading_name": "Ningbo", + "port_of_discharge_locode": "USLAX", + "port_of_discharge_name": "Los Angeles", + "pod_vessel_name": "EVER FORWARD", + "pod_vessel_imo": "9850551", + "pod_voyage_number": "1119E", + "destination_locode": "USCLE", + "destination_name": "Cleveland", + "destination_timezone": "America/New_York", + "destination_ata_at": null, + "destination_eta_at": "2024-07-02T08:00:00Z", + "pol_etd_at": null, + "pol_atd_at": "2024-06-09T03:42:00Z", + "pol_timezone": "Asia/Shanghai", + "pod_eta_at": null, + "pod_original_eta_at": null, + "pod_ata_at": "2024-06-22T13:36:00Z",