Search
Add Listing
  • You have no bookmark.

Your Wishlist : 0 listings

Sign In

PortChief API — Developer Documentation

PortChief API — Developer Documentation

Manage container listings server-to-server over a small REST API: authenticate once, then create, read, search, update and withdraw listings.

Base URL  https://portchief.com/wp-json/portchief/v1

ℹ️ The examples below use the full URL (with https:// and the host) — copy-paste them as-is, nothing to concatenate.

🚀 Quick start

  1. Exchange your PortChief credentials for a token at POST /auth/token.
  2. Send that token as Authorization: Bearer <token> on every call.
  3. Create listings with POST /listings.
  4. Read, search, update or withdraw them with GET / PATCH / DELETE /listings. Done.
💡 Prefer to click instead of code? Import our Postman collection (download below) — it auto-saves the token and ships ready-made example requests.

1 · Authentication

POST /auth/token

Exchange a WordPress username + password for a signed JWT. The token is valid 30 days.

Request

POST https://portchief.com/wp-json/portchief/v1/auth/token
Content-Type: application/json

{
  "username": "your-account",
  "password": "your-password"
}

Response · 200 OK

{
  "token": "eyJhbGciOiJIUzI1Ni␣...",
  "token_type": "Bearer",
  "expires_in": 2592000,
  "user": { "id": 4, "email": "you@example.com", "name": "Your Name" }
}

Bad credentials return 401 { "code": "pc_auth_failed" }.

Using the token

Authorization: Bearer eyJhbGciOiJIUzI1Ni␣...

2 · Create a listing

POST /listings

Creates one published container listing. One listing = one container (atomic). Your organization is taken from your account — never from the payload.

Required fields

Field Type Rule
direction * string offer or need
container_type * string 40HC · 40DC · 20DC · Reefer · Tank · Open Top · Flat Rack · Hazmat
city * string City/port slug (e.g. marseille-fos)
address or lat+lng * string / number At least one. An address alone is geocoded automatically (422 if unresolved).

Optional fields

Field Type Rule / default
pickup_window object { "start": "YYYY-MM-DD", "end": "YYYY-MM-DD" } — strict ISO 8601, start ≤ end. start defaults to today; end open if omitted.
price number Offers only. Default 0 (free). Currency is the site base currency (EUR).
description string Free text, ≤ 5000 chars
image_base64 string jpeg / png / webp, ≤ 5 MB (data-URI prefix accepted)
title string Auto-generated if omitted

Example request

POST https://portchief.com/wp-json/portchief/v1/listings
Authorization: Bearer <token>
Content-Type: application/json

{
  "direction": "offer",
  "container_type": "40HC",
  "city": "marseille-fos",
  "lat": 43.34, "lng": 4.99,
  "description": "Reefer-ready 40HC, clean box.",
  "pickup_window": { "start": "2026-06-20", "end": "2026-06-30" },
  "price": 150
}

Response · 201 Created

{
  "id": 659463,
  "url": "https://portchief.com/listing/offre-40hc-marseille-fos/",
  "status": "publish"
}

3 · Read a listing

GET /listings/{id}

Returns the full, enriched view of any published listing (so you can confirm what the server stored and reconcile your own system).

GET https://portchief.com/wp-json/portchief/v1/listings/659463
Authorization: Bearer <token>

Response · 200 OK

{
  "id": 659463,
  "url": "https://portchief.com/listing/offre-40hc-marseille-fos/",
  "status": "publish",
  "direction": "offer",
  "title": "Offre — 40HC · Marseille-Fos",
  "city": { "slug": "marseille-fos", "name": "Marseille-Fos" },
  "container_type": { "slug": "40-hc", "label": "40HC" },
  "coords": { "lat": 43.34, "lng": 4.99 },
  "pickup_window": { "start": "2026-06-20", "end": "2026-06-30" },
  "price": 150,
  "contact": { "email": "you@example.com", "phone": null, "website": null },
  "created_at": "2026-06-16T12:57:31+02:00",
  "updated_at": "2026-06-17T09:49:00+02:00"
}

Unknown / withdrawn id returns 404 { "code": "pc_not_found" }.

4 · Search listings

GET /listings

List and filter listings. Parameters go in the URL query string (a GET has no body): ?key=value&key2=value2.

Param Type Filter
direction string offer / need
city string city/port slug
container_type string 40HC, 20DC, Reefer…
pickup_from / pickup_to date ISO 8601 window on the pickup start
lat + lng + radius_km number Proximity search — listings within the radius, sorted by distance; each result then carries distance_km
mine bool mine=1only your own listings (the author comes from the token; withdrawn listings never appear)
page / per_page int Pagination (per_page default 20, max 100)
GET https://portchief.com/wp-json/portchief/v1/listings?direction=need&container_type=reefer&lat=43.34&lng=4.99&radius_km=50
Authorization: Bearer <token>

Response · 200 OK

{
  "results": [ { /* same shape as “Read a listing” */ "distance_km": 12.3 } ],
  "page": 1, "per_page": 20, "total": 7, "total_pages": 1
}

distance_km (km, 1 decimal) is included only in proximity search (lat+lng+radius_km) — plain searches and Read a listing don’t have it.

5 · Update a listing

PATCH /listings/{id}

Update one of your own listings (others return 403). Send only the fields you want to change; the listing id stays stable.

PATCH https://portchief.com/wp-json/portchief/v1/listings/659463
Authorization: Bearer <token>
Content-Type: application/json

{
  "pickup_window": { "start": "2026-07-01" }
}

Returns 200 with the updated listing (same shape as “Read”). Invalid fields → 400 with data.fields. Note: switching an offer to need drops its price.

6 · Withdraw a listing

DELETE /listings/{id}

Withdraw one of your own listings once the container is gone — it immediately disappears from the marketplace. Soft delete (recoverable), and idempotent: calling it again still returns 200.

DELETE https://portchief.com/wp-json/portchief/v1/listings/659463
Authorization: Bearer <token>

Response · 200 OK

{ "id": 659463, "status": "withdrawn" }

7 · Errors

HTTP Code Meaning
400 pc_validation Invalid payload. Offending fields are listed under data.fields.
401 pc_auth_failed / — Missing, invalid, or expired token.
403 pc_no_org / pc_forbidden Your account has no organization, or you tried to modify a listing that isn’t yours.
404 pc_not_found No such listing (unknown id, not a listing, or withdrawn).
422 pc_geocode_failed The address could not be located. Provide lat/lng or refine it.

Validation error example · 400

{
  "code": "pc_validation",
  "message": "Invalid listing payload.",
  "data": {
    "status": 400,
    "fields": {
      "pickup_window.start": "Invalid date '20226'. Use strict ISO 8601 YYYY-MM-DD."
    }
  }
}

Notes

  • Versioning — the API is namespaced /v1. New optional fields may be added without breaking changes; any breaking change ships under a new version.
  • Quantity — fixed at 1 per listing. To post N containers, send N requests.
  • Contact — buyers reach you through the contact details on your organization profile.
📦 Postman collection
⬇ Download the Postman collection
Import it into Postman for all 6 endpoints, automatic token capture, and ready-to-run examples (happy paths + every error case).