Getting Started

Emporia Post API

The Emporia Post REST API gives you programmatic access to multi-carrier shipping rates, label purchasing, address validation, and tracking — all through a single, consistent interface backed by the eHub carrier network.

Base URL

/api/public/v1

Auth

Bearer Token

Format

JSON

Base URL

https://your-domain.manus.space/api/public/v1

Replace your-domain with your Emporia Post deployment domain.

Authentication

All API requests (except GET /ping) require a valid API key passed as a Bearer token in the Authorization header.

Authorization: Bearer ep_live_YOUR_API_KEY

Scopes

Each API key is issued with one or more scopes that control which endpoints it can access.

ScopeEndpointsDescription
rates/rates, /addresses/validateFetch carrier rates and validate addresses
labels/labels (all methods)Purchase, retrieve, and void labels
tracking/tracking/:numberLook up tracking status and events
Security: Never expose your API key in client-side code or public repositories. Always make API calls from your server.

Rate Limits

Rate limits are enforced per API key using a sliding 60-second window. The default limit is 60 requests per minute. You can configure a higher limit (up to 300 req/min) when creating a key in the Developer Portal.

HTTP StatusConditionResponse
429Rate limit exceeded{ error: "rate_limit_exceeded", retryAfter: 60 }

When you receive a 429, wait retryAfter seconds before retrying. Implement exponential backoff for production systems.

Error Codes

All errors return a JSON body with error (machine-readable code) and message (human-readable description).

{
  "error": "unauthorized",
  "message": "Missing or malformed Authorization header. Expected: Bearer <api_key>"
}
StatusError CodeMeaning
200Success
201Resource created (label purchased)
400bad_requestMissing or invalid request parameters
401unauthorizedMissing, invalid, or revoked API key
403forbiddenAPI key lacks the required scope
404not_foundResource not found
429rate_limit_exceededToo many requests — slow down
500internal_errorUnexpected server error
503service_unavailableeHub API key not configured or service down

API Reference

Endpoints

GET/api/public/v1/ping

Returns the current API status and version. No authentication required. Use this to verify connectivity before making authenticated requests.

Response

FieldTypeDescription
statusstringAlways "ok" when the service is healthy
servicestringService name: "Emporia Post API"
versionstringCurrent API version string
timestampstringISO 8601 UTC timestamp of the response

Example

curl https://your-domain.manus.space/api/public/v1/ping
POST/api/public/v1/addresses/validatescope: rates

Validates a shipping address against the carrier network. Returns a corrected address and any validation messages. Call this before fetching rates or purchasing labels to reduce failed deliveries.

Request Body

FieldTypeRequiredDescription
addressobjectrequiredAddress object to validate (see Address Object below)
address.address1stringrequiredStreet address line 1
address.citystringrequiredCity name
address.statestringrequired2-letter state code (US)
address.countrystringrequired2-letter ISO country code (e.g. "US")
address.postal_codestringrequiredZIP or postal code

Response

FieldTypeDescription
data.validbooleanWhether the address is deliverable
data.addressobjectCorrected/normalized address object
data.messagesstring[]Validation warnings or correction notes

Example

curl -X POST https://your-domain.manus.space/api/public/v1/addresses/validate \
  -H "Authorization: Bearer ep_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "address": {
      "address1": "123 Main St",
      "city": "Austin",
      "state": "TX",
      "country": "US",
      "postal_code": "78701"
    }
  }'
POST/api/public/v1/ratesscope: rates

Returns available carrier rates for a shipment. Results are sorted by price ascending. Filter by carrier or service level on the client side using the carrier_code and service_code fields.

Request Body

FieldTypeRequiredDescription
fromAddressobjectrequiredSender address object
toAddressobjectrequiredRecipient address object
parcelobjectrequiredPackage dimensions and weight
parcel.lengthnumberrequiredLength in inches
parcel.widthnumberrequiredWidth in inches
parcel.heightnumberrequiredHeight in inches
parcel.weightnumberrequiredWeight in pounds

Response

FieldTypeDescription
dataRate[]Array of available service rates, sorted by price
data[].service_idnumberUse this ID to purchase a label
data[].carrierstringCarrier display name (e.g. "USPS")
data[].carrier_codestringMachine-readable carrier code
data[].servicestringService level name
data[].ratenumberCost in USD
data[].delivery_daysnumberEstimated transit days
countnumberTotal number of rates returned

Example

curl -X POST https://your-domain.manus.space/api/public/v1/rates \
  -H "Authorization: Bearer ep_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "fromAddress": {
      "address1": "123 Main St", "city": "Austin",
      "state": "TX", "country": "US", "postal_code": "78701"
    },
    "toAddress": {
      "address1": "456 Oak Ave", "city": "Denver",
      "state": "CO", "country": "US", "postal_code": "80203"
    },
    "parcel": { "length": 10, "width": 8, "height": 4, "weight": 1.5 }
  }'
POST/api/public/v1/labelsscope: labels

Purchases a shipping label for the selected service. Returns a label URL, tracking number, and cost. The label URL is valid for 24 hours — download and store it immediately.

Request Body

FieldTypeRequiredDescription
fromAddressobjectrequiredSender address object
toAddressobjectrequiredRecipient address object
parcelobjectrequiredPackage dimensions and weight
serviceIdnumberrequiredservice_id from a /rates response
labelFormatstringoptional"pdf" (default), "png", or "zpl"

Response

FieldTypeDescription
data.labelIdstringUnique label identifier (use for GET/DELETE)
data.trackingNumberstringCarrier tracking number
data.carrierstringCarrier code
data.servicestringService level name
data.costnumberAmount charged in USD
data.labelUrlstringTemporary URL to download the label file
data.statusstringInitial status: "purchased"

Example

curl -X POST https://your-domain.manus.space/api/public/v1/labels \
  -H "Authorization: Bearer ep_live_YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "fromAddress": { "address1": "123 Main St", "city": "Austin", "state": "TX", "country": "US", "postal_code": "78701" },
    "toAddress":   { "address1": "456 Oak Ave", "city": "Denver", "state": "CO", "country": "US", "postal_code": "80203" },
    "parcel":      { "length": 10, "width": 8, "height": 4, "weight": 1.5 },
    "serviceId":   12345,
    "labelFormat": "pdf"
  }'
GET/api/public/v1/labels/:labelIdscope: labels

Retrieves the current status and metadata for a previously purchased label. Use the labelId returned from the POST /labels endpoint.

Response

FieldTypeDescription
data.labelIdstringLabel identifier
data.trackingNumberstringCarrier tracking number
data.carrierstringCarrier code
data.statusstringCurrent status: purchased | in_transit | delivered | cancelled
data.costnumberAmount paid in USD
data.labelUrlstringLabel download URL
data.createdAtstringISO 8601 creation timestamp

Example

curl https://your-domain.manus.space/api/public/v1/labels/LABEL_ID \
  -H "Authorization: Bearer ep_live_YOUR_KEY"
DELETE/api/public/v1/labels/:labelIdscope: labels

Voids (cancels) a purchased label. Most carriers allow voiding within 30 days of purchase. Voided labels cannot be used for shipping. Refunds are subject to carrier policy.

Response

FieldTypeDescription
data.voidedbooleantrue if the label was successfully voided
data.labelIdstringThe label ID that was voided

Example

curl -X DELETE https://your-domain.manus.space/api/public/v1/labels/LABEL_ID \
  -H "Authorization: Bearer ep_live_YOUR_KEY"
GET/api/public/v1/tracking/:trackingNumberscope: tracking

Returns the current tracking status and event history for a shipment by tracking number. Works with any carrier supported by eHub.

Response

FieldTypeDescription
data.statusstringCurrent tracking status
data.trackingNumberstringThe tracking number queried
data.carrierstringCarrier code
data.eventsEvent[]Array of tracking events, newest first
data.events[].statusstringEvent status label
data.events[].descriptionstringHuman-readable event description
data.events[].locationstringLocation of the event
data.events[].event_timestringISO 8601 event timestamp

Example

curl https://your-domain.manus.space/api/public/v1/tracking/9400111899223397846059 \
  -H "Authorization: Bearer ep_live_YOUR_KEY"

Platform Guides

eBay Integration

Emporia Post connects to your eBay seller account via OAuth 2.0. Once authorized, orders are automatically imported and tracking numbers are pushed back to eBay to mark items as shipped — no manual copy-paste required.

Prerequisites

  • An active eBay seller account
  • An eBay Developer account at developer.ebay.com
  • An eBay application with sell.fulfillment and sell.fulfillment.readonly scopes

Setup Steps

  1. Go to the eBay Developer Portal → Application Keys and create a new application.
  2. Under User Tokens, create an OAuth redirect URL (RuName) pointing to: https://your-domain.manus.space/api/ebay/callback
  3. In Emporia Post, go to Settings → Secrets and add: EBAY_CLIENT_ID, EBAY_CLIENT_SECRET, EBAY_RU_NAME
  4. Navigate to Integrations and click Authorize with eBay. You will be redirected to eBay's consent screen.
  5. After authorizing, you will be redirected back to Emporia Post and the connection will be active.

How Sync Works

DirectionTriggerWhat happens
eBay → Emporia PostManual "Sync Orders" buttonFetches open orders from eBay Fulfillment API and imports them as platform orders
Emporia Post → eBayLabel purchasedCalls createShippingFulfillment with tracking number and carrier to mark item as shipped

Shopify Integration

Connect your Shopify store using a private app API key. Emporia Post imports unfulfilled orders and pushes fulfillment data (tracking number and carrier) back to Shopify when a label is purchased.

Setup Steps

  1. In your Shopify admin, go to Apps → Develop apps and create a new app.
  2. Under Configuration, grant the following Admin API scopes: read_orders, write_fulfillments
  3. Install the app and copy the Admin API access token.
  4. In Emporia Post, go to Integrations → Shopify → Connect and enter your store domain (e.g. my-store.myshopify.com) and API token.
DirectionAPI Used
Order importGET /admin/api/2024-01/orders.json?fulfillment_status=unfulfilled
Tracking pushPOST /admin/api/2024-01/orders/{id}/fulfillments.json

Etsy Integration

Connect your Etsy shop using the Etsy Open API v3. Emporia Post imports open receipts (orders) and pushes tracking numbers back to Etsy when a label is purchased.

Setup Steps

  1. Create an app at etsy.com/developers/register.
  2. Request the transactions_r and transactions_w scopes.
  3. Generate an API key and your Shop ID (visible in your Etsy shop URL).
  4. In Emporia Post, go to Integrations → Etsy → Connect and enter your API key and Shop ID.
DirectionAPI Used
Order importGET /v3/application/shops/{shopId}/receipts?was_shipped=false
Tracking pushPOST /v3/application/shops/{shopId}/receipts/{receiptId}/tracking

Poshmark Integration

Note: Poshmark does not offer a public seller API for third-party integrations. The Poshmark connection in Emporia Post uses a manual CSV import workflow.

CSV Import Workflow

  1. In your Poshmark account, go to My Sales and export your orders as a CSV.
  2. In Emporia Post, go to Integrations → Poshmark → Import CSV.
  3. Upload the CSV file. Emporia Post will map the columns and import the orders.
  4. After purchasing a label, copy the tracking number and manually update the Poshmark order to mark it as shipped.

CSV Column Mapping

Poshmark ColumnMaps To
Order IDplatformOrderId
Buyer UsernamebuyerName
Buyer AddresstoAddress.address1
Item TitleorderDetails
Sale PriceorderValue

ThredUp Integration

Note: ThredUp does not offer a public seller API for third-party integrations. The ThredUp connection in Emporia Post uses a manual CSV import workflow identical to the Poshmark flow.

Follow the same CSV import workflow as Poshmark. Export your ThredUp order data, import it into Emporia Post, purchase labels, and manually update ThredUp with the tracking numbers.

Generic Webhook Integration

The Generic Webhook connection lets any platform push orders to Emporia Post via HTTP POST, and receive tracking information back via an outbound webhook when a label is purchased.

Inbound Webhook (Orders → Emporia Post)

Send a POST request to your webhook endpoint with the order payload:

POST https://your-domain.manus.space/api/webhook/orders
X-Webhook-Secret: YOUR_WEBHOOK_SECRET
Content-Type: application/json

{
  "platformOrderId": "ORDER-12345",
  "buyerName": "Jane Smith",
  "buyerEmail": "[email protected]",
  "toAddress": {
    "address1": "456 Oak Ave",
    "city": "Denver",
    "state": "CO",
    "country": "US",
    "postal_code": "80203"
  },
  "orderValue": 49.99,
  "orderDetails": "Blue Vintage Jacket, Size M"
}

Outbound Webhook (Tracking → Your Platform)

When a label is purchased for a webhook order, Emporia Post sends a POST to your configured callback URL:

POST https://your-platform.com/emporia-callback
Content-Type: application/json

{
  "event": "label.purchased",
  "platformOrderId": "ORDER-12345",
  "trackingNumber": "9400111899223397846059",
  "carrier": "USPS",
  "service": "Priority Mail",
  "cost": 8.45,
  "labelUrl": "https://..."
}

Security

All inbound requests must include the X-Webhook-Secret header matching the secret shown in your Integrations settings. Requests without a valid secret are rejected with HTTP 401.