ShipBee API Documentation

Foundation endpoints for token auth, operational identity, clients, locations, and last-mile parcels.

Back home

Authentication

POST

/api/auth/login

Create an API token

Send an email and password. The demo owner account is [email protected] with password password.

{
  "email": "[email protected]",
  "password": "password"
}
GET

/api/auth/me

Fetch current identity

Requires Authorization: Bearer {token}. Returns user, organization, location, and roles.

POST

/api/auth/logout

Revoke current token

Requires Authorization: Bearer {token}. Deletes the token used for the request.

Identity Management

GET

/api/roles

List assignable roles

Owner/admin only. Returns the seeded Spatie roles such as admin, dispatcher, pickup_point_staff, client_admin, and client_user.

GET

/api/clients

List merchant clients

Owner/admin only. Supports optional is_active and search filters. Results are scoped to the authenticated user's organization.

POST

/api/clients

Create a merchant client

Creates a customer/merchant record inside the authenticated organization. Slugs are unique per organization.

{
  "name": "Demo Merchant",
  "slug": "demo-merchant",
  "contact_name": "Merchant Admin",
  "contact_email": "[email protected]"
}
GET

/api/users

List organization users

Owner/admin only. Supports optional role, location_id, client_id, and search filters.

POST

/api/users

Create a staff or client user

Operational users have client_id = null. Client users require client_id. Pickup point staff require location_id.

{
  "name": "Pickup Staff",
  "email": "[email protected]",
  "password": "password",
  "location_id": 5,
  "roles": ["pickup_point_staff"]
}
PATCH

/api/users/{user}

Update user assignment or roles

Owner/admin only. Keeps organization, location, client, and role assignments consistent with the access model.

Demo Scenario Data

CLI

shipbee:demo populate

Populate removable scenario data

Adds deterministic demo clients, staff users, pickup point users, locations, parcels, barcodes, parcel events, and location inventory movements.

./vendor/bin/sail artisan shipbee:demo populate
CLI

shipbee:demo clear

Clear only scenario data

Removes the deterministic scenario records while keeping base countries, roles, the demo organization, and the base admin/merchant accounts.

./vendor/bin/sail artisan shipbee:demo clear

Operations

GET

/api/countries

List operational countries

Requires Authorization: Bearer {token}. Returns seeded country records for Costa Rica, Panama, and Guatemala.

GET

/api/countries/{code}

Fetch one country

Country codes are two-letter ISO-style codes such as CR, PA, and GT.

GET

/api/locations

List tenant locations

Supports optional country_code and type filters. Results are scoped to the authenticated user's organization.

POST

/api/locations

Create a location

Allowed types are hub, warehouse, pickup_point, and crossdock. The country must match the organization country.

PATCH

/api/locations/{location}

Update a location

Locations are not deleted in this foundation; use is_active to disable them.

Location Inventory

GET

/api/locations/{location}/parcels

List current parcels at a location

Staff-only endpoint. Returns parcels where current_location_id matches the location. Supports status, delivery_method, barcode, and client_id filters.

GET

/api/locations/{location}/movements

List location movement ledger

Returns incoming and outgoing parcel movements for the location. Supports type, parcel_id, barcode, date_from, and date_to filters.

GET

/api/locations/{location}/inventory-summary

Fetch location inventory counts

Returns current parcel count, incoming today, outgoing today, ready for pickup count, and picked up today count.

Last-Mile Parcels

GET

/api/parcels

List parcels

Returns paginated results. Supports page, per_page up to 100, and filters for status, tracking_number, barcode, country_code, current_location_id, handover_method, delivery_method, and pickup_point_id.

POST

/api/parcels

Create a parcel

Creates a last-mile parcel with separate handover and delivery methods. Staff users may provide client_id; client users are always assigned to their own client. ShipBee generates tracking_number and a default system barcode when omitted; integrations that print their own labels may still provide optional tracking_number and barcodes.

{
  "handover_method": "pickup_requested",
  "pickup_requested_at": "2026-05-12 10:00:00",
  "delivery_method": "home_delivery",
  "recipient_name": "Maria Perez",
  "recipient_phone": "+506 2222 3333",
  "delivery_address_line_1": "Avenida Central 100",
  "delivery_city": "San Jose",
  "delivery_country_code": "CR"
}
GET

/api/parcels/{parcel}/labels/pdf

Download parcel PDF label

Returns a browser-printable 4x6 PDF label for an accessible parcel. The parcel response includes this URL at labels.pdf_url.

curl "https://api.ship.jorkvist.com/api/parcels/142/labels/pdf" \
  -H "Authorization: Bearer {token}" \
  -o shipbee-label.pdf
GET

/api/parcels/{parcel}/labels/zpl

Download parcel ZPL label

Returns a raw 203dpi Zebra ZPL label for the same 4x6 layout. The parcel response includes this URL at labels.zpl_url.

curl "https://api.ship.jorkvist.com/api/parcels/142/labels/zpl" \
  -H "Authorization: Bearer {token}" \
  -o shipbee-label.zpl
POST

/api/parcels/{parcel}/receive

Receive parcel into network

Sets received_at, received_location_id, and current_location_id. The receiving location must have can_receive_parcels enabled and the action writes a parcel_received event.

PATCH

/api/parcels/{parcel}

Update operational parcel fields

Updates non-destination fields only. Recipient, delivery method, home delivery address, and pickup point changes must use the explicit action endpoints so timeline events are always recorded.

POST

/api/parcels/{parcel}/change-delivery-address

Change home delivery address

Allowed only for home_delivery parcels. Updates the parcel delivery snapshot and creates a delivery_address_changed event with old and new address metadata.

POST

/api/parcels/{parcel}/change-pickup-point

Change pickup point

Requires a pickup point location in the authenticated user's organization. Updates pickup_point_id, sets delivery_method to pickup_point, and creates a pickup_point_changed event.

POST

/api/parcels/{parcel}/change-delivery-method

Change delivery method

Supports home_delivery to pickup_point and pickup_point to home_delivery. Creates a delivery_method_changed event.

POST

/api/parcels/{parcel}/change-recipient

Change recipient

Updates recipient snapshot fields and creates a recipient_changed event with old and new recipient metadata.

PATCH

/api/parcels/{parcel}/status

Update parcel status

Updates parcel status through the allowed transition map. Status changes write status_changed events with old/new state and optional metadata.

GET

/api/parcels/{parcel}/events

List parcel events

Returns the parcel timeline with event_type, occurred time, old values, new values, metadata, status transition fields, user, and location fields.

POST

/api/parcels/{parcel}/comments

Add a parcel comment

Stores operational notes for the parcel without adding customs or inspection workflows.

Pickup Points

GET

/api/pickup/parcels

List pickup parcels

Staff-only endpoint for the currently logged-in organization. Returns pickup point parcels that are already ready_for_pickup and physically at their assigned pickup point. Pickup point staff are automatically limited to their assigned location_id.

Optional filters: pickup_point_id, client_id, barcode, and pickup_code. The barcode filter matches either the parcel tracking_number or a related barcode record.

GET /api/pickup/parcels?pickup_point_id=5&barcode=SB-CR-000142
Authorization: Bearer {token}
{
  "data": [
    {
      "id": 142,
      "organization_id": 1,
      "client_id": 3,
      "country_code": "CR",
      "current_location_id": 5,
      "delivery_method": "pickup_point",
      "pickup_point_id": 5,
      "pickup_point_location_id": 5,
      "pickup_code": "482913",
      "pickup_code_generated_at": "2026-05-16T15:04:22.000000Z",
      "pickup_code_expires_at": "2026-05-30T15:04:22.000000Z",
      "pickup_ready_at": "2026-05-16T15:04:22.000000Z",
      "picked_up_at": null,
      "picked_up_by_name": null,
      "tracking_number": "SB-CR-000142",
      "status": "ready_for_pickup",
      "recipient_name": "Maria Perez",
      "recipient_phone": "+506 2222 3333",
      "recipient_email": "[email protected]",
      "delivery_address": {
        "line_1": "Avenida Central 100",
        "line_2": null,
        "city": "San Jose",
        "state": null,
        "postal_code": null,
        "country_code": "CR",
        "latitude": null,
        "longitude": null,
        "instructions": "Ask for ID before handover."
      },
      "pickup_point": {
        "id": 5,
        "name": "Alajuela Pickup",
        "code": "ALA-PUP",
        "type": "pickup_point",
        "city": "Alajuela",
        "can_be_pickup_point": true
      },
      "barcodes": [
        {
          "id": 201,
          "parcel_id": 142,
          "barcode": "SB-CR-000142",
          "type": "primary"
        }
      ]
    }
  ]
}
POST

/api/pickup/scans/receive

Receive at pickup point

Scans a pickup parcel into a pickup-point-capable location. The parcel must use delivery_method = pickup_point and be assigned to the submitted location_id. The action generates a 6-digit pickup_code if one does not already exist, sets pickup_ready_at, and marks the parcel ready_for_pickup.

Required body fields: barcode and location_id. Pickup point staff may only receive into their assigned location. Optional geolocation stores the event coordinates in parcel event and movement metadata; include latitude and longitude, with optional accuracy_meters and captured_at.

{
  "barcode": "SB-CR-000142",
  "location_id": 5,
  "geolocation": {
    "latitude": 9.932542,
    "longitude": -84.079578,
    "accuracy_meters": 12.5,
    "captured_at": "2026-05-16T15:04:20Z"
  }
}
{
  "data": {
    "id": 142,
    "current_location_id": 5,
    "delivery_method": "pickup_point",
    "pickup_point_id": 5,
    "pickup_code": "482913",
    "pickup_code_generated_at": "2026-05-16T15:04:22.000000Z",
    "pickup_code_expires_at": "2026-05-30T15:04:22.000000Z",
    "pickup_ready_at": "2026-05-16T15:04:22.000000Z",
    "picked_up_at": null,
    "tracking_number": "SB-CR-000142",
    "status": "ready_for_pickup",
    "recipient_name": "Maria Perez",
    "pickup_point": {
      "id": 5,
      "name": "Alajuela Pickup",
      "code": "ALA-PUP",
      "type": "pickup_point",
      "can_be_pickup_point": true
    },
    "events": [
      {
        "event_type": "arrived_at_pickup_point",
        "location_id": 5,
        "metadata": {
          "pickup_point_id": 5,
          "geolocation": {
            "latitude": 9.932542,
            "longitude": -84.079578,
            "accuracy_meters": 12.5,
            "captured_at": "2026-05-16T15:04:20Z"
          }
        }
      },
      {
        "event_type": "pickup_code_generated",
        "location_id": 5,
        "metadata": {
          "pickup_code": "482913",
          "pickup_point_id": 5,
          "geolocation": {
            "latitude": 9.932542,
            "longitude": -84.079578,
            "accuracy_meters": 12.5,
            "captured_at": "2026-05-16T15:04:20Z"
          }
        }
      },
      {
        "event_type": "ready_for_pickup",
        "location_id": 5,
        "metadata": {
          "pickup_point_id": 5,
          "geolocation": {
            "latitude": 9.932542,
            "longitude": -84.079578,
            "accuracy_meters": 12.5,
            "captured_at": "2026-05-16T15:04:20Z"
          }
        }
      }
    ]
  }
}
POST

/api/pickup/scans/handover

Hand over parcel

Hands a ready pickup parcel to the consignee. Submit either barcode or pickup_code, plus the pickup point and consignee handover details. The parcel must be ready_for_pickup at the submitted pickup_point_id.

Required body fields: pickup_point_id, picked_up_by_name, and either barcode or pickup_code. Optional: picked_up_by_document and geolocation. On success the action stores the handover details, sets picked_up_at, clears current_location_id, marks the parcel picked_up, and stores event coordinates in parcel event and movement metadata.

{
  "pickup_code": "482913",
  "pickup_point_id": 5,
  "picked_up_by_name": "Maria Perez",
  "picked_up_by_document": "CR-01-1234-0567",
  "geolocation": {
    "latitude": 9.93612,
    "longitude": -84.08364,
    "accuracy_meters": 8
  }
}
{
  "data": {
    "id": 142,
    "current_location_id": null,
    "delivery_method": "pickup_point",
    "pickup_point_id": 5,
    "pickup_code": "482913",
    "pickup_ready_at": "2026-05-16T15:04:22.000000Z",
    "picked_up_at": "2026-05-16T16:18:09.000000Z",
    "picked_up_by_name": "Maria Perez",
    "picked_up_by_document": "CR-01-1234-0567",
    "picked_up_by_user_id": 12,
    "tracking_number": "SB-CR-000142",
    "status": "picked_up",
    "recipient_name": "Maria Perez",
    "events": [
      {
        "event_type": "pickup_code_verified",
        "location_id": 5,
        "metadata": {
          "pickup_code": "482913",
          "pickup_point_id": 5,
          "verification_method": "pickup_code",
          "geolocation": {
            "latitude": 9.93612,
            "longitude": -84.08364,
            "accuracy_meters": 8
          }
        }
      },
      {
        "event_type": "parcel_picked_up",
        "location_id": 5,
        "metadata": {
          "picked_up_by_name": "Maria Perez",
          "pickup_point_id": 5,
          "geolocation": {
            "latitude": 9.93612,
            "longitude": -84.08364,
            "accuracy_meters": 8
          }
        }
      }
    ]
  }
}

Response Shape

{
  "token": "...",
  "user": {
    "id": 1,
    "organization_id": 1,
    "location_id": 1,
    "client_id": null,
    "name": "ShipBee Admin",
    "email": "[email protected]"
  },
  "organization": {
    "id": 1,
    "name": "ShipBee Costa Rica",
    "slug": "shipbee-cr",
    "country_code": "CR",
    "timezone": "America/Costa_Rica",
    "currency_code": "CRC",
    "is_active": true
  },
  "location": {
    "id": 1,
    "organization_id": 1,
    "country_code": "CR",
    "name": "San José Hub",
    "code": "SJO-HUB",
    "type": "hub",
    "city": "San José",
    "is_active": true
  },
  "roles": ["owner"]
}

Access Model

ShipBee Staff

  • users.client_id is null
  • Can see parcels for their organization
  • May select client_id when creating parcels

Client Users

  • users.client_id is set
  • Can only see parcels for their client
  • Cannot assign parcels to another client

Parcel Ownership

  • organization_id is operational ownership
  • client_id is customer ownership
  • Both are enforced on customer-side access

Foundation Model

Country

  • Operational reference data
  • Owns organizations and locations by code
  • No tariffs, customs, or HS codes

Organization

  • Tenant boundary
  • One operational country/company
  • Has many locations and users

Client

  • Belongs to one organization
  • Owns client users and parcels
  • Represents merchants using ShipBee

Location

  • Belongs to one organization
  • Types: hub, warehouse, pickup_point, crossdock
  • Has many users

User

  • Belongs to an organization
  • May belong to a location
  • Uses Sanctum tokens and Spatie roles

Parcel

  • Scoped to organization and country
  • Tracks current location and polymorphic timeline events
  • Supports home delivery and pickup point delivery
  • Uses inline delivery recipient and address snapshot fields
  • Requires explicit action endpoints for destination changes