How Portifer works,
step by step.
Public help and developer documentation for Portifer: how a campaign runs from setup to feedback, how to import data, how shipping works, and the webhooks and APIs you can call today.
Guide
Run your first campaign
A Portifer campaign moves through five steps. Each one happens inside the same campaign, so status and responses stay attached to the right recipient.
- Create your account at portifer.com/register with your work email, then add your products to the catalogue.
- Create a campaign, choose the campaign type (seeding, testing, outreach or occasion) and build the box with products, variants and inserts.
- Add recipients: import a CSV or Excel file, import from HubSpot, add them through a box link or the box API, or invite testers from UserLabs.
- Ship: connect a carrier account under Settings → Carriers and print labels, or let your fulfilment partner ship on a managed plan. Recipients confirm their address on a branded page first.
- Collect feedback: after delivery, recipients answer your survey and upload photos or video. Responses are summarised and sentiment-scored on the campaign page.
Guide
Import data
The import centre is under Dashboard → Data. Companies and contacts accept CSV or .xlsx up to 10 MB with suggested column mapping; recipient lists accept CSV or pasted text up to 1,000 rows; box recipients accept CSV up to 5,000 rows with a downloadable template.
Guide
Shipping and tracking
Connect Bring (Posten), PostNord, DHL Express, UPS or FedEx under Dashboard → Settings → Carriers using your own carrier account's API credentials. Portifer tests the connection, stores the credentials encrypted, and lets you limit each account to certain countries. Labels and tracking then live on each recipient in the campaign.
Developers
Outbound webhooks
Create webhook subscriptions under Dashboard → Settings → Integrations. Choose events (campaign.started, campaign.completed, tester.enrolled, submission.created, submission.approved) and optionally limit a subscription to one campaign. The signing secret is shown once and can be rotated.
Each delivery is a JSON POST with a 10-second timeout. Failed deliveries are retried up to three attempts in total, waiting 2 and then 4 seconds. Every attempt is logged in the deliveries view. URLs that resolve to internal network addresses are rejected.
POST https://your-app.example.com/portifer-webhook
Content-Type: application/json
User-Agent: Portifer-Webhooks/1.0
X-Webhook-Event: submission.created
X-Webhook-ID: <subscription id>
X-Webhook-Signature: <hex HMAC-SHA256 of the body>
{
"event": "submission.created",
"timestamp": "2026-10-04T09:30:00.000Z",
"data": { "...": "event-specific fields" },
"metadata": { "campaignId": "...", "clientId": "..." }
}Developers
Verify webhook signatures
X-Webhook-Signature is the hex-encoded HMAC-SHA256 of the raw request body, keyed with your subscription secret. There is no prefix and no timestamp; verify against the exact bytes you received, before parsing the JSON.
import crypto from "node:crypto"
export function isValidPortiferWebhook(rawBody: string, signature: string | null, secret: string) {
if (!signature) return false
const expected = crypto.createHmac("sha256", secret).update(rawBody, "utf8").digest("hex")
const a = Buffer.from(expected, "hex")
const b = Buffer.from(signature, "hex")
return a.length === b.length && crypto.timingSafeEqual(a, b)
}Developers
Inbound: sign testers up to a campaign
Each campaign can have its own signup webhook (campaign page → Webhook tab). POST a tester's email and name (or firstName/lastName, optional phone) with the campaign's key in the X-Webhook-Key header. Portifer creates the tester if needed and enrols them, without creating duplicates. Requests are limited to 100 per minute per IP address.
curl -X POST https://portifer.com/api/webhooks/campaigns/<campaignId>/signup \
-H "Content-Type: application/json" \
-H "X-Webhook-Key: <campaign webhook key>" \
-d '{ "email": "[email protected]", "name": "Kari Nordmann", "phone": "+4712345678" }'Developers
Inbound: add recipients to a box
Generate a box API token (starts with bx_) from the box builder's API dialog. POST recipients to the box with companyName and contactName (required) plus optional email, phone, jobTitle, street, city, postalCode, country, notes and tags. A GET on the same URL lists recipients with limit (max 200) and offset. Each token is limited to 100 requests per minute; a duplicate email in the same box returns 409.
curl -X POST https://portifer.com/api/boxes/public/<bx_token>/recipients \
-H "Content-Type: application/json" \
-d '{
"companyName": "Nordic Example AS",
"contactName": "Kari Nordmann",
"email": "[email protected]",
"street": "Storgata 1",
"city": "Oslo",
"postalCode": "0155",
"country": "NO"
}'Developers
What is not available
- There is no general-purpose public REST API or SDK for campaigns, shipments or responses.
- Webhooks do not yet include shipment, delivery or meeting events; use the Slack integration for those.
- API keys for other endpoints are issued by our team on request as part of a pricing agreement.
Frequently asked questions
Does Portifer have a public API?
Portifer has signed outbound webhooks, an inbound campaign signup webhook and a box recipients API. There is no general-purpose REST API for campaigns, shipments or responses.
How do I get help with setup?
Email [email protected] or use the contact page. Assisted and Fully Managed customers work with our team on setup directly.
Related pages
Last updated 2026-10-04
Stuck on a step?
Tell us what you are trying to set up and we'll walk you through it.