Builder API
Use Pelusium from your own dApp to publish Cargo Well asks, list collateralized haul jobs, read the open job board, and share flight numbers (PELU-…) with couriers. Settlement and protocol fees are enforced on-chain when a job is created and delivered.
This page is for integrators (wallets, marketplaces, companion apps). Player guides live under Introduction. Interactive HTTP reference (Try it): Builder API reference.
You do not wait on world-contract A-to-B inventory to integrate. Partner asks, wallet proofs, routing, and dryRun: true work today on Stillness. Dry runs show as Preview on Cargo Well and the job board for ~15 minutes but do not lock SUI or settle. Pickup and deliver still use the same Pelusium haul steps once a real job exists.
What to build first
| Your UI | Pelusium API | Needs on-chain SUI? |
|---|---|---|
| Sell on Pelusium (any storefront) | POST /api/shop/partner-listings/ | No — wallet proof only. Use dryRun: true until you are ready to publish. |
| Inbound freight on a hub page | GET /api/logistics/jobs/?dropoff=<ssu> | No — public read. |
| Buy & deliver / haul | createHaulIntent then sign create_haul_job | Yes, on confirm. Use dryRun: true first to check route and PTB args. |
No Pelusium store registration. source is your app id (for example my-shop or your-app-id — pick one stable string per integration).
Overview
| Layer | Role |
|---|---|
@pelusium/sdk | Wallet-proof helpers, HTTP calls, and Sui Programmable Transaction Blocks (create, accept, pickup, deliver). |
| Pelusium backend | Indexes jobs, pathfinding, async listing intents, partner asks, public reads. |
Move (haul_contract) | Escrow, courier bond, treasury fees, delivery rules. |
Your storefront --wallet proof--> partner listing (Cargo Well ask)
Buyer / your app --sign PTB------> haul_contract (Sui)
|
v
Pelusium indexer --> job board + flight PELU-…
Base URL
All requests use the Pelusium API host shown at the top of the interactive reference. Paths such as /api/logistics/jobs/ are appended to that host (production: https://api.pelusium.world).
Install the SDK
npm install @pelusium/sdk @mysten/sui
Package: npmjs.com/package/@pelusium/sdk.
Network defaults: STILLNESS_TESTNET (packageId, protocolConfigId, haulConfigId).
Dry run (integrator test suite)
Use dry run before locking SUI on a live haul. You get the same routes and proofs with no SUI spent.
| What you test | How |
|---|---|
| Payload + SSU ownership + Cargo Well row | Partner listing with dryRun: true — publishes a Preview ask |
| Route, fees, PTB build args | createHaulIntent with dryRun: true, then poll until ready |
| Public reads | GET jobs, listings, prices, flights — or click Try it on the reference |
Dry-run hauls follow processing → ready / failed. They cannot be confirmed. Ready dry-run hauls appear on the job board as status: preview with flight PELU-DRY-… (TTL 24 hours on the sandbox deploy via LOGISTICS_DRY_RUN_TTL_SECONDS; default elsewhere is 15 minutes).
Dry-run partner listings write a Cargo Well Preview row (preview: true, expiresAt). Everyone sees Preview · your-app-id; use Preview Buy & deliver to start a dry-run haul (wallet proof only — no SUI locked). Preview asks do not match live bids or decrement on a real haul. Same source + externalListingId without dryRun upgrades that row to a live ask.
Sandbox click-through (preview delivery loop)
On the sandbox line, after a dry-run haul is ready, advance rehearsal stages with wallet proofs only:
| Stage | Proof action |
|---|---|
accepted | logistics.sandbox.accept |
pickup_approved | logistics.sandbox.approve_pickup |
picked_up | logistics.sandbox.pickup |
delivered | logistics.sandbox.deliver |
POST /api/logistics/intents/{intentId}/sandbox/advance/
{ "stage": "accepted", "proof": { ... } }
The shipper wallet may walk every stage for a solo demo. Another wallet may accept, becomes courier, and must perform pickup/deliver. GET /api/logistics/flights/PELU-DRY-… resolves preview flights. Confirm stays 409.
You still sign a wallet proof (shop.partnerListing.upsert or logistics.intent.create). That proof does not move funds.
import {
buildWalletProofMessage,
createHaulIntent,
PELUSIUM_WALLET_PROOF_ACTIONS,
upsertPartnerCargoListing,
waitForLogisticsIntent,
} from "@pelusium/sdk"
const BACKEND = "https://api.pelusium.world"
// 1) Preview ask on Cargo Well (no live match)
const listingCheck = await upsertPartnerCargoListing(
BACKEND,
{
source: "your-app-id",
externalListingId: "dry-1",
sellerWallet: wallet,
pickupSsuId,
pickupSystemId: 300001,
typeId: 12345,
quantity: 10,
priceSui: 50,
characterId,
ownerCapId,
dryRun: true,
},
listingProof, // action: shop.partnerListing.upsert
)
console.log(listingCheck.valid)
// 2) Validate a haul (route + buildArgs, no confirm)
const pending = await createHaulIntent(BACKEND, {
proof: createProof, // action: logistics.intent.create
wallet,
characterId,
pickupSsuId,
dropoffSsuId,
pickupSystemId: 101,
dropoffSystemId: 202,
typeId: 12345,
quantity: 10,
freightMist: 10_000_000,
goodsMist: 50_000_000_000,
requireBond: false,
source: "your-app-id",
dryRun: true,
})
const ready = await waitForLogisticsIntent(BACKEND, pending.intentId)
console.log(ready.status, ready.route?.summary, ready.buildArgs)
Dry run proves your integration. It does not prove in-game pickup or deliver. Those need a real signed haul and Pelusium authorized on both SSUs. On Stillness, live jobs use collateralized haul today; that is enough to test the API. Exact sealed-bag world delivery is a later game install — do not block your Sell on Pelusium button on it.
Sell on Pelusium from any storefront
Any storefront can publish an ask into Cargo Well without registering a Pelusium store. Buyers Buy & deliver; freighters run the normal haul.
- Seller taps Sell on Pelusium on a hangar stack (not a warehouse receipt).
- Seller signs
shop.partnerListing.upsert(no SUI). POST /api/shop/partner-listings/withsource,externalListingId,pickupSsuId,pickupSystemId,typeId,quantity, andpriceSui(total SUI for the full quantity; in-game-only currencies are not accepted).- Keep the returned listing
id. Samesource+externalListingIdupdates the row. - If the pickup SSU is not Pelusium-authorized, deep-link SSU setup (
buildAuthorizePelusiumExtensionTx). Pickup fails until they do.
Check: GET /api/shop/listings/?source=your-app-id&store_id=0x…
Cancel: POST /api/shop/partner-listings/{id}/cancel/ with shop.partnerListing.cancel.
Browser vs server: POST from your backend needs no CORS. POST from the browser requires your site origin to be allowlisted for Pelusium API CORS. There is no partner API key — the wallet proof is the auth.
Do not list units still sitting as exchange receipts. Redeem to hangar first, or a courier cannot pick up.
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",
"requireBond": false,
"bondOfGoodsPercent": 0,
"dryRun": false
},
"proof": { "address": "0x…", "message": "…", "signature": "…" }
}
Shared SSUs need characterId and ownerCapId. Pelusium verifies the signer owns that SSU.
SDK: upsertPartnerCargoListing, cancelPartnerCargoListing, buildWalletProofMessage.
When a buyer uses Buy & deliver, a verified on-chain create event (matching seller, pickup, cargo, quantity, positive goods escrow) can decrement the partner listing.
Wallet proofs (write endpoints)
POST routes require a wallet proof: the user signs a short UTF-8 message. The proof does not move funds.
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.
{
"proof": {
"address": "0x…",
"message": "<full message string>",
"signature": "<base64 signature>"
}
}
SDK: buildWalletProofMessage and PELIUSIUM_WALLET_PROOF_ACTIONS.
| Action | Used for |
|---|---|
shop.partnerListing.upsert | POST /api/shop/partner-listings/ |
shop.partnerListing.cancel | POST /api/shop/partner-listings/{id}/cancel/ |
logistics.intent.create | POST /api/logistics/intents/create-haul/ |
logistics.intent.confirm | POST /api/logistics/intents/{intentId}/confirm/ |
logistics.bounty.accept | POST /api/logistics/flights/{flightNumber}/bounty/ |
logistics.bounty.select | POST /api/logistics/flights/{flightNumber}/bounty/select/ (dry-run only) |
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: dropoff, pickup, limit, max_hops, shipper) |
| GET | /api/logistics/prices/?type_id= | Implied goods SUI per unit and freight stats (type_id required; cached ~30s) |
| GET | /api/logistics/flights/{PELU-…}/ | Flight: route, lifecycle, digests |
| GET | /api/logistics/bounty-board/ | Jobs where both sides opted into bounty visibility |
| GET | /api/shop/listings/?source=&store_id=&type_id= | Cargo Well asks. source= is your app id |
Jobs with goods_mist > 0 contribute Cargo Well market prints alongside shop listings.
Async haul listing (live)
After a dry run looks good, omit dryRun (or set false). Pelusium computes a route, then the shipper signs on-chain:
POST /api/logistics/intents/create-haul/— prooflogistics.intent.create, wallet, both SSU ids, both solar system ids,typeId,quantity,freightMist, optionalgoodsMist, optionalrequireBond/bondOfGoodsPercent, optionalsource. DefaultgenerateRoute: true. HTTP 202 +intentId. Courier bond is 0 unless the seller set a portion and goods are at least 5 SUI.requireBond: trueis 110% of goods;bondOfGoodsPercent(1–500) sets a custom portion. Clients cannot inventrequiredBondMist, and listing-backed creates use the listing’s portion.GET /api/logistics/intents/{intentId}/— poll untilready,failed, orexpired.buildCreateHaulJobTxFromIntent(ready.buildArgs)— user signs and executes.POST /api/logistics/intents/{intentId}/confirm/—digest, wallet, prooflogistics.intent.confirm. ReturnsflightNumber(PELU-…).
Ready intents include buildArgs, route (distanceLy, hopCount, hops, summary), and fee inputs.
import {
buildCreateHaulJobTxFromIntent,
confirmLogisticsIntent,
createHaulIntent,
waitForLogisticsIntent,
} from "@pelusium/sdk"
const BACKEND = "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,
goodsMist: 50_000_000_000,
requireBond: false,
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: 0n,
})
Authorize each SSU once with buildAuthorizePelusiumExtensionTx. Typical courier path: buildAcceptHaulJobTx → pickup owner buildApproveHaulPickupTx → buildExecuteHaulPickupTx → buildExecuteHaulDeliverTx.
Grand Exchange (goods + freight books)
Match mode is goods first, freight rests. Settlement is always create_haul_job.
| Read | Meaning |
|---|---|
GET /api/shop/buy-orders/ | Goods bids. Matchable rows have dropoffSsU and freightMist. ?wallet= for a shipper’s bids. |
GET /api/shop/matches/ | Pending crosses (bid ≥ ask, dest SSU present). fillQty is the slice. |
GET /api/shop/freight-asks/ | Courier standing asks. |
GET /api/logistics/jobs/?status=open&shipper= | Open hauls after a goods cross. |
Poll GET /api/shop/matches/ and deep-link the buyer to Pelusium Prefill haul. The matcher does not create a haul for you. Partial fills reduce listing and bid quantity; one haul per slice.
Pickup and delivery (same rules as the Pelusium UI)
Partner listings and intents are discovery and routing. They do not move cargo. After a haul exists on-chain, every integrator 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
Patterns: list via API and deep-link to www.pelusium.world for haul UX; or embed the SDK and prompt each PTB. Hybrid is fine.
Goods escrow pays the goods_seller wallet — 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:
POST /api/logistics/flights/{flightNumber}/bounty/
Proof action logistics.bounty.accept plus the accept transaction digest. The flight appears on /api/logistics/bounty-board/ only when both sides opt in.
On sandbox dry-run flights (PELU-DRY-*), courier opt-in uses proof only (no digest). Hunters select a target with logistics.bounty.select on POST /api/logistics/flights/{flightNumber}/bounty/select/. After simulated delivery, bounties resolve — courier on success, selected hunter on failure.
Fees and limits
- Protocol fees are charged in Move at create/settle. No separate API billing tier.
- Writes are rate-limited. Prefer dry run while integrating.
- The insurance HTTP API is not part of this builder surface.
Prerequisites
- Sui wallet (proofs). Network SUI only when you confirm a live haul.
- Pelusium extension authorized on pickup and drop-off SSUs before pickup/deliver.
- Package / config ids for your network (
STILLNESS_TESTNETon Stillness).
Partner app field reference
Map your marketplace or companion app fields to Pelusium API payloads before you call Sell on Pelusium from any storefront.
| Your app / API | Pelusium field | Notes |
|---|---|---|
| Pickup storage unit id | pickupSsuId | On-chain SSU object id where cargo sits today |
| Drop-off storage unit id | dropoffSsuId | Buyer’s delivery SSU on haul create |
| Commodity id (integer) | typeId | Must match the EVE Frontier item type |
| App identifier | source | Stable string you choose once per integration |
| Your listing id | externalListingId | Updates the same Cargo Well row when reused with the same source |
Pelusium lists partner asks and haul jobs; it does not run your marketplace’s internal order book or convert non-SUI balances for you.
Example reads (replace placeholders):
GET /api/logistics/jobs/?status=open&dropoff=<storageUnitId>
GET /api/logistics/flights/{PELU-…}/
GET /api/shop/listings/?source=your-app-id
Routing and pathfinding need public solar system ids on both pickup and drop-off when you create a haul intent.
Links
- This guide: pelusium.world/docs/builder-api
- Try it (OpenAPI): pelusium.world/docs/builder-api/reference
- Player guides: pelusium.world/docs/introduction