Inbox placement
Inbox placement
Tests where a message sent from a given domain actually lands. The module sends nothing on the caller's behalf — the real message is sent to fifteen seed inboxes, and the module reports back where each one ended up: primary inbox, a secondary tab (Promotions, Social), or spam.
The result is a snapshot of how a recipient mailbox treated this exact message, from this exact domain, at this exact moment. It is not a simulation, a reputation lookup, or a header inspection. The module answers one question: if this message is sent from this domain right now, where does it go?
Inputs
A test job takes two values.
| Field | Required | Description |
|---|---|---|
domain |
yes | The sending domain to test, lowercased, without the @ (for example acme.fr). Must contain a dot and be 3 to 120 characters. |
subject_filter |
no | Optional substring matched against the seed inbox subject lines. Useful to disambiguate when multiple tests run in parallel from the same domain. Up to 120 characters. |
The module does not take a recipient list. Inbox placement is a standalone job — it is not part of a pipeline and cannot consume the output of another job.
Outputs
The job writes one row per seed mailbox to results_delivery.csv. Fifteen seed inboxes are queried; each row describes what that mailbox observed.
| Column | Description |
|---|---|
seed_email |
Address of the test inbox the row refers to. |
seed_kind |
Provider family for the seed (used to group results by mailbox type). |
status |
received if the message was found, otherwise an empty or pending state. |
placement |
Where the message landed: Inbox principal, Inbox · <tab> (for example Promotions, Social), Spam, or empty if not received. |
subject |
Subject line as observed in the seed mailbox. |
received_relative |
Human-readable delay between send and observation (for example 2 min). |
The structured report endpoint aggregates these rows into a summary.
| Field | Description |
|---|---|
received |
Number of seeds that observed the message. |
total |
Total seed inboxes queried (15). |
missing |
total - received. |
primary |
Seeds where placement is Inbox principal. |
primary_pct |
Primary inbox rate as a percentage of received. |
inbox_secondary |
Seeds where placement is a non-primary inbox tab. |
promotions |
Seeds where placement matched Promotions or Social. |
spam |
Seeds where placement is Spam. |
spam_pct |
Spam rate as a percentage of received. |
verdict |
Contextual verdict — see below. |
seeds |
The per-seed array described in the previous table. |
The verdict object carries a one-line judgment and an actionable note.
verdict.label |
When |
|---|---|
EXCELLENT |
primary_pct ≥ 90. |
TRÈS BON |
primary_pct ≥ 70. |
MOYEN |
primary_pct ≥ 50. |
MAUVAIS |
spam_pct ≥ 50. |
INSUFFISANT |
Most messages landed in secondary tabs. |
EN ATTENTE |
Nothing received yet. |
Lifecycle
Standard job states — see Jobs lifecycle. The runtime workflow is: create the job, fetch seeds via GET /api/delivery-check/seeds, send the real message to all fifteen from the domain under test, wait for the worker to poll until all seeds report received or a timeout fires, then read the aggregated report.
The module does not chain. Its output is not reusable as input for another job — Inbox placement is listed in the non-chainable job set alongside viewport_test.
Pipeline
Inbox placement is standalone_only.
- Needs: nothing. The job takes a domain string, not a list of records.
- Produces: no reusable columns. The CSV exists for export but is not exposed to the pipeline graph.
- Pipelinable: no.
- Veille: not supported.
If a campaign needs to react to a placement result, the report is consumed from the API and branched on in external orchestration — the module will not feed another node directly.
Endpoints
| Method | Path | Purpose |
|---|---|---|
POST |
/api/jobs/delivery-check |
Create a delivery-check job. Body: { "domain": "...", "subject_filter": "..." }. Returns the public job object. |
GET |
/api/delivery-check/seeds |
List the fifteen seed addresses to send the test message to. |
GET |
/api/jobs/{job_id}/delivery-result |
Aggregated report with summary, verdict, and per-seed rows. |
GET |
/api/jobs/{job_id} |
Standard job status (queued, running, done, failed). |
All endpoints require an authenticated, active user. Reading another user's job returns 403.
Budget per job is fixed at fifteen seed observations; no slider, no override. Inbox placement does not consume scraping credits (ef_per_item: 0), though the per-user job quota still applies. A complete run typically lands between two and eight minutes after the seed message is sent. The domain must contain a dot and is lowercased internally. For global caps, see Limits.
Errors
| Status | Reason |
|---|---|
400 |
Domaine d'envoi invalide — the domain is empty or does not contain a dot. |
400 |
Quota dépassé — MAX_EF_PER_JOB reached. Inbox placement itself is free, but the quota check still applies. |
400 |
Pas un job de test de délivrabilité — /delivery-result was called on a job whose type is not delivery_check. |
403 |
The job belongs to another user. |
404 |
The job ID does not exist. |
410 |
The job's CSV has expired and been deleted. |
If the report returns received: 0 after the worker has run, the seed message never arrived — either it was not sent, was blocked entirely, or the domain is on a complete blocklist. Re-send to the seeds and re-poll before drawing a conclusion.
What's next
- Verify emails — clean a list of addresses before sending, so that the seed test reflects what the deliverable subset will see.
- Ads intelligence — once placement is solid, see which competitors are paying for visibility on the same audience.