Opper / Mahalo connector
Inklura provides Mahalo with an API compatible with Opper Mail's transactional operations: authentication, domains, templates, sending and events. Mahalo initiates the send; Inklura submits it through its sending pipeline; actual SMTP provider feedback is forwarded to Mahalo in Opper's event format.
This page describes the Inklura connector, its HTTP contract and acceptance checks. It does not document the entire Opper Mail service.
Status on 12 October 2026: preproduction. A send from Mahalo's interface displayed ENVOYÉ, then DÉLIVRÉ following real Postmark feedback. Open, click, bounce and complaint events are connected and covered by tests; their display in Mahalo still requires acceptance testing with actual events. Mahalo production and recette environments are not yet provisioned for this connector.
Configure Mahalo
Request credentials from Inklura for the publisher, site and environment being connected. Each key belongs to one Inklura account and one site; it cannot access another site's templates or messages.
| Setting | Value |
|---|---|
| Connector protocol | Opper Mail |
| Preproduction base URL | https://mahalo-mail.inklura.fr/v1/ |
Client identifier / username |
Identifier supplied by Inklura |
API key / password |
Private key supplied separately by Inklura |
| Sender | Address approved for the Inklura site |
| Publisher reference | ref_editeur from the same Mahalo environment |
Keep the /v1/ suffix. A Brevo-compatible API key or an Inklura console login token cannot replace the connector key. Never put secrets in a URL, public ticket or screenshot.
After configuring the account, run Mahalo's connection test, check domains and templates, and send an email to a controlled test contact. A successful connection test alone does not validate status tracking.
Sending and delivery flow
sequenceDiagram
participant M as Mahalo
participant I as Inklura connector
participant P as SMTP provider
participant D as Recipient mail server
M->>I: Token, then transactional/send
I->>P: Email and correlation metadata
P-->>I: Send accepted
I-->>M: HTTP 200 and messageId
I->>M: request webhook
Note over M: ENVOYÉ
P->>D: Deliver email
D-->>P: Acceptance or failure
P->>I: Actual delivery or bounce feedback
I->>M: delivered or bounce webhook
Note over M: DÉLIVRÉ observed for actual delivery
ENVOYÉ means the sending pipeline accepted the message and Mahalo received the request event. DÉLIVRÉ means the recipient's mail server accepted the message. It does not guarantee inbox placement or reading. A measured open is a separate event.
HTTP contract
Paths below are relative to the base URL. Requests and responses use JSON. Supported operations return HTTP 200 on success.
| Method | Path | Response |
|---|---|---|
POST |
/token |
{ "token": "…" } |
GET |
/transactional/domains |
Array of approved domains |
GET |
/transactional/templates |
Array of active site templates |
POST |
/transactional/send |
{ "messageId": "…" } |
GET |
/newsletters/lists |
Array of site lists, read only |
Templates and lists expose id, name, created_at and updated_at, with ISO 8601 dates. Template identifiers belong to their site: retrieve them through this endpoint rather than reusing another account's identifiers.
Get a token
Body of POST /token:
{
"username": "INKLURA_CLIENT",
"password": "PRIVATE_KEY"
}
Tokens expire after one hour. Revoking the underlying key immediately invalidates its tokens. Supply X-Ownpage-Client-Token on subsequent operations and request a new token after expiry.
Example using a private credentials file containing the JSON above:
OPPER_BASE='https://mahalo-mail.inklura.fr/v1'
curl --fail-with-body --silent --show-error \
-H 'Content-Type: application/json' \
--data-binary @opper-credentials.json \
"$OPPER_BASE/token"
The dedicated ingress also accepts the legacy Mahalo client's GET /token with a JSON body. This adaptation applies only to the token operation; GET bodies are not accepted for other operations. Dedicated listener responses include Content-Length so the legacy client receives JSON without raw chunked-transfer markers.
Send content generated by Mahalo
curl --fail-with-body --silent --show-error \
-H "X-Ownpage-Client-Token: $OPPER_TOKEN" \
-H 'Content-Type: application/json' \
--data-binary @opper-message.json \
"$OPPER_BASE/transactional/send"
Example opper-message.json — fictional addresses and content, to replace for a controlled test:
{
"sender": { "email": "sender@example.com", "name": "My publisher" },
"to": [{ "email": "test@example.net", "name": "Test contact" }],
"subject": "Your request is confirmed",
"htmlContent": "<p>Hello Jane, your request is confirmed.</p>",
"textContent": "Hello Jane, your request is confirmed.",
"tags": ["mahalo-acceptance"]
}
Example response:
{ "messageId": "0123456789abcdef01234567" }
messageId is a 24-character hexadecimal string. Mahalo must retain it and correlate it with message-id in events. The SMTP provider's identifier is separate and must not replace the connector identifier.
Accepted fields: subject, sender, to, cc, bcc, replyTo, headers, attachment, htmlContent, textContent, templateId, params and tags. Optional fields set to null by Mahalo are treated as absent; unknown fields are rejected. Recipients use arrays of { "email": "…", "name": "…" } objects, with an optional name. attachment is an array of objects, for example [{ "name": "document.pdf", "content": "BASE64_CONTENT" }], or an object with an allowed url inside that array. A standalone attachment object is rejected.
The sender must match the site's configured sender or belong to a domain authenticated for that site. Suppression lists still apply. The temporary restriction to test recipients was removed after delivery acceptance testing.
Use an Inklura template
{
"templateId": 12,
"to": [{ "email": "test@example.net" }],
"params": { "FIRST_NAME": "Jane", "REFERENCE": "DEMO-123" },
"tags": ["mahalo-acceptance"]
}
12 is an example: use an identifier returned by /transactional/templates. The template must be active and belong to the key's site. When templateId is set, inline HTML/text is ignored in favor of the template.
Inklura templates support syntax such as {{ params.FIRST_NAME }}. Opper's newsletter component language is not emulated. Unresolved Mahalo markers such as #PRENOM# in the subject or content are rejected before sending: fix the template or generation context.
Events sent to Mahalo
Inklura configures a destination per connector key, using the corresponding publisher reference. These are callback receivers, not sending URLs:
| Mahalo environment | Event destination |
|---|---|
| Preproduction | https://api-preprod.mahalo-app.io/aboweb/oppermail?ref_editeur=REF |
| Production, to be provisioned | https://api.mahalo-app.io/aboweb/oppermail?ref_editeur=REF |
| Recette, to be provisioned | https://api-recette.mahalo-app.io/aboweb/oppermail?ref_editeur=REF |
REF must come from the target environment. The validated preproduction publisher is 2207; do not copy this reference into a future environment without checking it.
Inklura sends HTTP POST callbacks with Content-Type: application/json; charset=utf-8. Mahalo should acknowledge acceptance with a 2xx status. Unicode may be represented as \uXXXX escapes; after JSON decoding, subjects and tags retain their original text.
| Opper event | Source in this connector | Additional fields |
|---|---|---|
request |
Message accepted by Inklura's sending pipeline | No additional required field emitted |
delivered |
Actual SMTP provider delivery feedback | email |
unique_opened / opened |
First / subsequent open reported by the provider | email, user_agent when available |
click |
Click reported by the provider | email, link, user_agent when available |
soft_bounce / hard_bounce |
Recognized bounce feedback | email, message when available |
spam |
Complaint reported by the provider | email, message when available |
error / blocked / invalid_email |
Recognized provider failure type | email, message when available |
Example delivery envelope; all values are fictional:
{
"event": "delivered",
"date": "2026-10-12 14:51:10 UTC",
"ts_epoch": 1791816670000,
"message-id": "0123456789abcdef01234567",
"subject": "Your request is confirmed",
"tags": ["mahalo-acceptance"],
"email": "test@example.net"
}
date uses UTC; ts_epoch uses milliseconds, not seconds. Click events also include a link, for example "https://example.com/confirmation". Bounce events include the provider diagnostic in message. Do not infer delivery from the sending API's HTTP 200 alone.
Reliability and correlation
The request event waits about three seconds after send acceptance to allow Mahalo to persist its journal. Each transmission makes up to four attempts with increasing delays after failures. A process interruption may interrupt these request attempts: this path does not yet use a durable outbox.
For Postmark feedback, Inklura validates callback authentication, the allowed key, configured SMTP stream, recorded message and exact recipient. Unrelated shared-stream messages and verification fixtures are ignored. Native state and the event are persisted before forwarding to Mahalo. Processing or forwarding failures return 503 to Postmark to allow retries; an already forwarded callback is acknowledged without incrementing native counters again.
Mahalo must tolerate duplicates: an interruption between its response and Inklura's recorded acknowledgement can cause retransmission. Events may also arrive with delays; do not assume a strict arrival order.
Opper messages enable open and link tracking individually, without changing shared-stream defaults. An open depends on image loading and may originate from a proxy; it is not certain evidence of a person reading the email. Automatic replies and unknown bounce types are not converted into delivery failures.
Acceptance testing and validation status
The 12 October 2026 test ran from the Mahalo preproduction interface to a controlled test recipient. The journal displayed ENVOYÉ at 16:51:09 Europe/Paris, then DÉLIVRÉ at 16:51:10, after the recipient's mail server accepted the message and the Postmark callback was received.
| Check | Status |
|---|---|
| Mahalo connection and usable token response | Verified |
| Mahalo send and ENVOYÉ status | Verified end to end |
| Actual delivery and DÉLIVRÉ status | Verified end to end |
| Accents and Unicode subjects | Verified on a new send |
| Opens, clicks, bounces and complaints | Webhooks configured; mappings and retries tested; actual Mahalo acceptance testing remains |
| Connector tests and mail regressions | 157 tests passed |
| Production and recette | To provision when available |
For another acceptance test, send from Mahalo to a controlled contact, retain the messageId, and compare Mahalo's journal with callbacks. A direct Inklura API request does not prove a Mahalo journal was created. Next test an open and a click on a test link; use a controlled provider procedure for bounces without generating complaints against real email.
Errors and troubleshooting
| Result or symptom | Check |
|---|---|
400 |
JSON, accepted fields, required content, template identifier, unresolved Mahalo markers |
401 |
Client identifier, key, token header, expiry or revocation |
403 |
Approved sender, authenticated domain, or an acceptance restriction still active on another account |
404 |
Correct base URL or an active template in the correct site |
413 |
Request body above 1 MiB, including base64 attachments |
429 |
Listener limit of 120 requests per minute; honor Retry-After |
501 |
Operation not implemented by this connector |
502 |
Sending pipeline did not accept the message; retain any returned identifier for diagnosis |
500 |
Internal error requiring Inklura diagnosis |
HTTP 200, journal still EN_COURS |
Check the request callback, ref_editeur, environment and matching message-id |
| ENVOYÉ without DÉLIVRÉ | Check actual provider feedback and correlation; do not fabricate a status |
| Token received, no subsequent Mahalo call | Check HTTP decoding and the JSON response, not just HTTP 200 |
If sending times out or the outcome is uncertain, check the journal and native pipeline before resending: the connector does not promise general exactly-once sending. For an incident, supply Inklura with the environment, publisher reference, timestamp with timezone, messageId and HTTP statuses, without the key or token.
Compatibility scope
This integration covers the operations in the HTTP table and events listed above. Newsletter writes, list replacement, scheduling, messageVersions, the unsubscribed event, message mirrors (mirror_link) and general subscription/renewal workflows are outside the currently validated contract. Other authenticated endpoints return 501; unknown send fields return 400.
Protocol references: Opper Mail transactional sending, Opper Mail events, Postmark delivery feedback. For Inklura's other available protocol, see Brevo-compatible API.