Publishers
Manage traffic sources and lead vendors — publisher CRUD, pause/resume ingestion, volume caps and quality thresholds, per-publisher pricing, and delivery statistics.
A publisher is a source of inbound items — a traffic source or lead vendor that submits Work into Ductor. It is the supply-side counterpart to a recipient: a recipient is a Worker a routing decision selects, while a publisher is an origin that feeds work in. Managing a publisher means controlling how much it may submit, the quality bar its submissions must clear, what you pay for accepted items, and whether it is currently allowed to submit at all — the entry gate where work is admitted before it is priced and routed.
Where it lives
Publishers are served by PublishersService under /api/publishers; the
service implementation is in modules/services/publisher/. Every operation is
tenant-scoped and gated by publisher:read, publisher:write, or
publisher:delete.
The publisher object
Prop
Type
The current_* counters are read-only usage against the matching *_cap. Caps
and the quality threshold are the two levers that shape ingestion: a cap bounds
how much a publisher may send, the threshold bounds what quality is accepted.
Create and configure
CreatePublisher — POST /api/publishers (publisher:write) — registers a
source with its caps, quality bar, and revenue share. Only name is required;
caps default to 0 (unlimited).
curl -s -X POST https://api.ductor.io/api/publishers \
-H "Authorization: Bearer $DUCTOR_TOKEN" \
-H "X-Tenant-ID: $DUCTOR_TENANT_ID" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Lead Source",
"daily_cap": 1000,
"weekly_cap": 5000,
"monthly_cap": 20000,
"quality_threshold": 0.7,
"revenue_share": 15.0,
"tags": ["north-america", "tier1"]
}'A duplicate name returns a conflict; an out-of-range quality threshold or other business-rule violation returns an unprocessable-entity error rather than being silently clamped.
GetPublisher (GET /api/publishers/{publisher_id}, publisher:read) returns
one publisher including its live usage counters; ListPublishers
(GET /api/publishers, publisher:read) paginates the tenant's publishers
newest-first with pagination.page_size (max 1000, default 50) and
pagination.page_token. UpdatePublisher
(PUT /api/publishers/{publisher_id}, publisher:write) edits name, caps,
threshold, revenue share, and tags.
DeletePublisher (DELETE /api/publishers/{publisher_id}, publisher:delete)
permanently removes a publisher. Items it has already submitted are not affected.
Pause and resume ingestion
Rather than deleting a misbehaving source, pause it. PausePublisher —
POST /api/publishers/{publisher_id}/pause (publisher:write) — stops the
publisher from accepting new routable items; items already in flight are
unaffected. ResumePublisher (POST .../resume, publisher:write) re-enables
it.
curl -s -X POST https://api.ductor.io/api/publishers/$PUBLISHER_ID/pause \
-H "Authorization: Bearer $DUCTOR_TOKEN" \
-H "X-Tenant-ID: $DUCTOR_TENANT_ID"Pause and resume are guarded against no-ops: pausing an already-paused publisher (or resuming one that isn't paused) returns an unprocessable-entity error, so the status transition is always explicit.
Pricing
Each publisher carries its own monetization settings for accepted items.
GetPublisherPricing (GET /api/publishers/{publisher_id}/pricing,
publisher:read) returns the current configuration; UpdatePublisherPricing
(PUT .../pricing, publisher:write) upserts it.
Prop
Type
curl -s -X PUT https://api.ductor.io/api/publishers/$PUBLISHER_ID/pricing \
-H "Authorization: Bearer $DUCTOR_TOKEN" \
-H "X-Tenant-ID: $DUCTOR_TENANT_ID" \
-H "Content-Type: application/json" \
-d '{ "publisher_id": "'"$PUBLISHER_ID"'", "price_per_accepted_lead": 55.0, "currency": "USD" }'Statistics
GetPublisherStats — GET /api/publishers/{publisher_id}/stats
(publisher:read) — returns volume and quality metrics for a publisher: how much
it submitted, how much was accepted or rejected, its acceptance rate, average
quality, and the revenue it generated.
Prop
Type
Read acceptance_rate and average_quality together against the publisher's
quality_threshold to decide whether a source is worth its caps and revenue
share, or whether to tighten the threshold, pause it, or retire it.
Operations at a glance
| Operation | Method & path | Scope |
|---|---|---|
| List publishers | GET /api/publishers | publisher:read |
| Create publisher | POST /api/publishers | publisher:write |
| Get publisher | GET /api/publishers/{publisher_id} | publisher:read |
| Update publisher | PUT /api/publishers/{publisher_id} | publisher:write |
| Delete publisher | DELETE /api/publishers/{publisher_id} | publisher:delete |
| Pause publisher | POST /api/publishers/{publisher_id}/pause | publisher:write |
| Resume publisher | POST /api/publishers/{publisher_id}/resume | publisher:write |
| Get pricing | GET /api/publishers/{publisher_id}/pricing | publisher:read |
| Update pricing | PUT /api/publishers/{publisher_id}/pricing | publisher:write |
| Get statistics | GET /api/publishers/{publisher_id}/stats | publisher:read |
Where to go next
Secrets, Variables & Contexts
The tenant config and data plane — encrypted Secrets, plaintext Variables, and the composite-keyed Context JSON store — with a disambiguation table and the Context-store name-collision warning.
Billing & Usage
How Ductor treats money, budgets, and usage — the settled stage of the clearing lifecycle across four metering and pricing planes that must not be conflated.