3U

Remote HTTP Provider

Detailed reference for the primary integration pattern.

The Plugin HTTP Contract

Three HTTP communication paths:

Path A: External Service → ThreeU (Inbound Webhook)

Your service sends events to ThreeU when something happens.

Path B: Dashboard/POS → ThreeU → Plugin (Gateway)

A user triggers an action that ThreeU forwards to your service.

Path C: ThreeU → Remote Plugin (Outbound Action)

ThreeU calls your service directly for automated actions.

Path A: Inbound Webhook

POST /api/plugins/{id}/webhook

  • Public route — no auth:sanctum required
  • Protected by signature verification
  • Protected by throttle:webhook
  • Plugin resolved by path id
  • Brand resolved by brand_id body or X-Brand-ID header
  • Signature in X-Plugin-Signature header
  • Secret is per-install brand_plugins.webhook_secret
Inbound Webhook — Node.js
TypeScript
const crypto = require('crypto');

const payload = JSON.stringify({
  event: "shipment.delivered",
  brand_id: 123,
  data: { shipment_id: "shp_abc123" }
});

const timestamp = Math.floor(Date.now() / 1000);
const signature = crypto
  .createHmac('sha256', WEBHOOK_SECRET)
  .update(timestamp + '.' + payload)
  .digest('hex');

await fetch('https://api.threeu.app/api/plugins/42/webhook', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-Plugin-Signature': `sha256=${signature}`,
    'X-Plugin-Timestamp': String(timestamp),
    'X-Brand-ID': '123'
  },
  body: payload
});

Path B: Gateway Action

{GET|POST|PUT|DELETE} /api/plugins/{idOrSlug}/gateway/{action}

  • Authenticated (requires auth:sanctum)
  • Uses plugin.access middleware
  • Checks brand permissions and enabled actions
  • Used for manual/on-demand actions

Path C: Outbound Action

Handled by RemoteHttpProvider::onAction() internally:

  • Uses manifest.provider.base_url + action path
  • Signs requests with HMAC-SHA256
  • Configurable timeouts per action
  • Logs delivery status
  • Parses and returns response
Handling Outbound Actions
TypeScript
// Your service receives:
app.post('/actions/shipping/get-rates', (req, res) => {
  verifyThreeUSignature(req);
  const { order, destination } = req.body;
  const rates = calculateShippingRates(order, destination);
  res.json({ success: true, rates });
});