mailnix / REST: POST /v1/messages

    API reference

    REST: POST /v1/messages

    The canonical send endpoint. A drop-in for the SendGrid, Postmark, and SES REST shape: swap the base URL and the bearer, keep your code.

    POST /v1/messages

    Accepts a JSON message envelope. Returns a trace_id immediately and continues the routing decision + provider attempt asynchronously. The trace ID is public-shareable.

    POST https://api.mailnix.ch/v1/messages
    Authorization: Bearer mnx_live_…
    Content-Type:  application/json

    Request body

    {
      "to":              ["jordan@acme.com"],
      "cc":              [],
      "bcc":             [],
      "from":            "noreply@example.com",
      "reply_to":        "support@example.com",
      "subject":         "Your receipt",
      "text":            "Plain-text body",
      "html":            "<p>HTML body</p>",
      "destination_id":  null,
      "template_id":     null,
      "template_vars":   {},
      "send_at":         null,
      "idempotency_key": "checkout-7c2d-receipt",
      "attachments": [
        {
          "filename":     "receipt.pdf",
          "content":      "<base64>",
          "content_type": "application/pdf"
        }
      ]
    }

    destination_id is optional; omit it and mailnix picks a destination from your routing rules. template_id + template_vars render a saved Sprig template server-side. send_at is an RFC3339 timestamp up to 3 days in the future (Pro+ only).

    The outcome field

    The response carries an outcome string that tells you exactly what mailnix did with the message:

    • relayed: sent to a real provider.
    • captured.anonymous_project: the project is anonymous; capture only.
    • captured.sandbox_destination: the matched destination is in sandbox mode.
    • captured.no_route: no routing rule matched; the project's no_route_policy was capture.

    Idempotency

    Pass an idempotency_key on retry-prone callers (checkout, signup, webhook worker). mailnix indexes the key per project for 24 hours; a duplicate POST returns the original trace ID instead of producing a second send.

    Scheduled sends

    Set send_at to an RFC3339 timestamp in the future (max 3 days, Pro+). The response carries the same shape but with status: "queued"; the actual provider attempt happens at send_at. Cancel via DELETE /v1/messages/scheduled/<trace_id>.

    Errors

    Every error has the same envelope shape:

    {
      "error": {
        "code":        "validation_failed",
        "message":     "from address is missing",
        "next_action": "supply a verified sender or set destination_id"
      }
    }

    Common codes: validation_failed, quota_exceeded, quota_exhausted_captures (HTTP 402), destination_misconfigured, provider_rate_limited, insufficient_scope.