Builder API
Use Pelusium from your own dApp to list collateralized haul jobs, read the open job board, publish partner Cargo Well asks, and share flight numbers (PELU-…) with couriers. Settlement and protocol fees are enforced on-chain when jobs are created and delivered.
This page is for integrators (wallets, marketplaces, companion apps). Player guides live under Introduction.
Overview
| Layer | Role |
|---|---|
@pelusium/sdk | Build Sui Programmable Transaction Blocks (create, accept, pickup, deliver) and call the Pelusium HTTP API. |
| Pelusium backend | Indexes on-chain events, pathfinding for routes, async listing intents, partner listings, and public read APIs. |
Move (haul_contract) | Escrow, courier bond, treasury fees, and delivery rules. |
Your dApp --sign PTB--> haul_contract (Sui)
|
v
Pelusium backend --> /api/logistics/*
|
v
Cargo Well (market prints)
Base URL
All requests use the Pelusium API host (see the API base URL at the top of the interactive reference). Paths such as /api/logistics/jobs/ are appended to that host.
Install the SDK
npm install @pelusium/sdk @mysten/sui
Package: npmjs.com/package/@pelusium/sdk.
Network defaults: STILLNESS_TESTNET (packageId, protocolConfigId, haulConfigId).
Wallet proofs (write endpoints)
Several POST routes require a wallet proof: the user signs a short UTF-8 message with their Sui wallet. The proof does not move funds; it authorizes an off-chain action (intent creation, confirmation, or partner listing).
Message format (lines must match exactly):
Nebulas Logistics database authorization
Address: 0x…
Action: <action-id>
Issued At: <ISO-8601 timestamp>
Nonce: <unique string>
This signature only authorizes a database write. It cannot move funds.
Send the proof in JSON as:
{
"proof": {
"address": "0x…",
"message": "<full message string>",
"signature": "<base64 signature>"
}
}
The SDK exports buildWalletProofMessage and PELIUSIUM_WALLET_PROOF_ACTIONS for the standard action ids.
| Action | Used for |
|---|---|
logistics.intent.create | POST /api/logistics/intents/create-haul/ |
logistics.intent.confirm | POST /api/logistics/intents/{intentId}/confirm/ |
shop.partnerListing.upsert | POST /api/shop/partner-listings/ |
shop.partnerListing.cancel | POST /api/shop/partner-listings/{id}/cancel/ |
logistics.bounty.accept | POST /api/logistics/flights/{flightNumber}/bounty/ |
Proofs expire after about 10 minutes. Nonces are single-use.
Public read API
| Method | Path | Description |
|---|---|---|
| GET | /api/logistics/jobs/?status=open&kind=haul&type_id= | Open haul jobs (optional filters: limit, max_hops) |
| GET | /api/logistics/prices/?type_id= | Implied goods SUI per unit and freight stats (type_id required; cached ~30s, 30/min per IP) |
| GET | /api/logistics/flights/{PELU-…}/ | Flight detail: route, lifecycle, digests |
| GET | /api/logistics/bounty-board/ | Jobs where shipper and courier opted into bounty visibility |
Jobs with goods_mist > 0 contribute Cargo Well market prints alongside shop listings.
Async haul listing
Recommended flow when you want Pelusium to compute a route before the shipper signs on-chain:
POST /api/logistics/intents/create-haul/— Body includesproof(logistics.intent.create), wallet, both SSU ids, both solar system ids, cargotypeId,quantity,freightMist,requiredBondMist, optionalgoodsMist, optionalsource, optionalsellerAllowsBounty. DefaultgenerateRoute: true. Returns HTTP 202 withintentIdandpollUrl.GET /api/logistics/intents/{intentId}/— Poll untilstatusisready,failed, orexpired.- Build the create transaction with SDK
buildCreateHaulJobTxFromIntent(ready.buildArgs); user signs and executes on Sui. POST /api/logistics/intents/{intentId}/confirm/— Body:digest,wallet,proof(logistics.intent.confirm). ReturnsflightNumber(PELU-…).
When status is ready, the intent includes buildArgs, route (distanceLy, hopCount, hops, summary), and fee inputs for the PTB.
SDK example
import {
STILLNESS_TESTNET,
buildCreateHaulJobTxFromIntent,
confirmLogisticsIntent,
createHaulIntent,
waitForLogisticsIntent,
} from "@pelusium/sdk"
const BACKEND = process.env.PELUSIUM_BACKEND_URL ?? "https://api.pelusium.world"
const pending = await createHaulIntent(BACKEND, {
proof: createProof,
wallet,
characterId,
pickupSsuId,
dropoffSsuId,
pickupSystemId: 101,
dropoffSystemId: 202,
typeId: 12345,
quantity: 10,
freightMist: 10_000_000,
requiredBondMist: 5_000_000,
goodsMist: 50_000_000_000,
source: "your-app-id",
})
const ready = await waitForLogisticsIntent(BACKEND, pending.intentId)
if (ready.status !== "ready" || !ready.buildArgs) throw new Error(ready.error ?? "Not ready")
const tx = buildCreateHaulJobTxFromIntent(ready.buildArgs)
const { digest } = await signAndExecuteTransaction({ transaction: tx })
const confirmed = await confirmLogisticsIntent(BACKEND, pending.intentId, digest, wallet, confirmProof)
console.log(confirmed.flightNumber)
Direct on-chain create (no intent)
If you already know both SSUs and do not need Pelusium route metadata on the board:
import { STILLNESS_TESTNET, buildCreateHaulJobTx } from "@pelusium/sdk"
const tx = buildCreateHaulJobTx({
packageId: STILLNESS_TESTNET.packageId,
protocolConfigId: STILLNESS_TESTNET.protocolConfigId,
haulConfigId: STILLNESS_TESTNET.haulConfigId,
freightPaymentMist: 10_000_000n,
goodsPaymentMist: 0n,
pickupSsU: "0x…",
dropoffSsU: "0x…",
typeId: 12345n,
quantity: 10,
slaDurationMs: 86_400_000n,
requiredBondMist: 5_000_000n,
})
Authorize each SSU once with buildAuthorizePelusiumExtensionTx. Typical courier path after create: buildAcceptHaulJobTx → pickup owner buildApproveHaulPickupTx (may also run while job is still open) → buildExecuteHaulPickupTx → buildExecuteHaulDeliverTx.
Dry run
Set dryRun: true on createHaulIntent (or on partner listing payloads) to validate inputs, routing, and PTB build args without publishing a listing or submitting a transaction. Dry-run intents follow the same poll lifecycle but cannot be confirmed, appear on the job board, or feed Cargo Well. They expire after about 15 minutes (expiresAt in the response).
Grand Exchange (goods + freight books)
Pelusium match mode is goods first, freight rests. Settlement is always create_haul_job (SDK buildCreateHaulJobTx / buildCreateHaulJobTxFromIntent) — there is no separate order-escrow Move module.
| Read | Meaning |
|---|---|
GET /api/shop/buy-orders/ | Goods bids. Matchable rows have dropoffSsU and freightMist. ?wallet= lists a shipper’s resting bids. |
GET /api/shop/matches/ | Pending crosses (bid ≥ ask, dest SSU present). fillQty is the partial-fill slice. Nested listing + buy order. |
GET /api/shop/freight-asks/ | Courier standing asks (min freight, optional type / max hops). |
GET /api/logistics/jobs/?status=open&shipper= | Open hauls that are the freight book after a goods cross. |
When a new listing or bid lands, the matcher (match_shop_books) upserts pending crosses. Your dApp can poll GET /api/shop/matches/ (fillQty, nested listing + bid) and deep-link the buyer to Pelusium Prefill haul. The matcher does not call POST /api/logistics/intents/create-haul/ for you (that route still needs the buyer’s wallet proof). If the buyer already has a live signed create-haul intent (same type and dropoff SSU), the matcher binds that intent — pickup, fillQty, freight, listing/bid ids — and re-queues the listing worker. Confirm the signed create as usual.
Partial fills reduce listing and buy-order quantity; one haul per slice. shop.applyListingAfterFreightJobPurchase (wallet proof) can attach buyOrderId and haulJobId after create.
Courier fill of a freight ask is still a signed accept_haul_job when Open-haul freight ≥ the ask (Prefill may set designated courier to the earliest matching ask). After accept, the indexer marks that courier’s matching ask filled.
Partner Cargo Well listings
List cargo in Cargo Well without creating a haul yourself. The seller signs shop.partnerListing.upsert, then:
POST /api/shop/partner-listings/
Content-Type: application/json
{
"payload": {
"source": "your-app-id",
"externalListingId": "listing-123",
"externalUrl": "https://your.app/listings/123",
"sellerWallet": "0x…",
"pickupSsuId": "0x…",
"pickupSystemId": 300001,
"pickupSystemName": "Stillness",
"characterId": "0x…",
"ownerCapId": "0x…",
"typeId": 12345,
"quantity": 10,
"priceSui": 50,
"note": "Optional description"
},
"proof": { "address": "0x…", "message": "…", "signature": "…" }
}
priceSui is the total for the full quantity. Repeating the same source + externalListingId updates the row. source is your attribution label.
Cancel with POST /api/shop/partner-listings/{pelusiumListingId}/cancel/ and proof action shop.partnerListing.cancel.
SDK helpers: upsertPartnerCargoListing, cancelPartnerCargoListing.
When a buyer uses Pelusium Buy & deliver, a verified on-chain create event with matching seller, pickup, cargo, quantity, and goods escrow can decrement the partner listing.
Pickup and delivery (same rules as the Pelusium UI)
Partner listings and async intents are discovery and routing helpers. They do not move cargo or bypass SSU gates. After a haul job exists on-chain, every integrator—Trinary, a wallet, or Pelusium itself—uses the same Move steps:
| Step | Who signs | SDK helper | Notes |
|---|---|---|---|
| Authorize Pelusium on SSU | Pickup and drop-off owners (once per SSU) | buildAuthorizePelusiumExtensionTx | Required before pickup/deliver |
| Create haul job | Shipper (often buyer) | buildCreateHaulJobTx or buildCreateHaulJobTxFromIntent | Locks freight + optional goods escrow |
| Accept job | Courier | buildAcceptHaulJobTx | Locks bond |
| Approve pickup | Pickup SSU OwnerCap holder (seller) | buildApproveHaulPickupTx | Anti-raid — partner API cannot skip |
| Execute pickup | Courier | buildExecuteHaulPickupTx | Needs freighter character id |
| Execute deliver | Courier | buildExecuteHaulDeliverTx | Drop-off SSU must be authorized |
Partner listing (HTTP) → Cargo Well row only
Buyer Buy & deliver / intent → create_haul_job (on-chain)
Seller → approve_haul_pickup
Courier → accept → execute_pickup → execute_deliver
Integration patterns:
- Pelusium-hosted legs — List via API; deep-link buyers to
www.pelusium.worldfor create, approve, pickup, and deliver UX. - Embedded SDK — Your app builds and prompts for each PTB; users still need EVE characters and SSU authorization in-game.
- Hybrid — Partner app for listing + checkout prefills; couriers and sellers use Pelusium Shipping for approve/pickup/deliver.
Goods escrow on create pays the goods_seller wallet you set in the listing or intent—the same wallet that should approve pickup when it owns the pickup SSU.
Bounty board
On create intent, set sellerAllowsBounty. After the courier accepts on-chain, they may opt in:
POST /api/logistics/flights/{flightNumber}/bounty/
Body includes proof action logistics.bounty.accept and the finalized accept transaction digest. The flight appears on /api/logistics/bounty-board/ only when both sides have opted in.
Fees and limits
- Protocol fees (treasury, goods fee, creation fee) are charged in Move at create/settle time. There is no separate API billing tier.
- Write endpoints are rate-limited per deployment. Use dry run for integration testing instead of hammering create routes.
- The insurance HTTP API is reserved for the Pelusium app and is not part of this builder surface.
Prerequisites
- Sui wallet and network SUI for escrow, bond, and gas
- Pelusium extension authorized on pickup and drop-off SSUs
- Correct package / config object ids for your network (
STILLNESS_TESTNETon the default Stillness testnet deployment)
Links
- Builder API: pelusium.world/docs/builder-api
- Player guides: pelusium.world/docs/introduction