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.

Note

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.