Back to Skills
    🦞

    unione

    Send transactional and marketing emails via UniOne Email API.

    By @andythemartketing
    View on GitHub
    SKILL.md
    ---
    name: unione
    description: >
      Send transactional and marketing emails via UniOne Email API.
      Manage email templates, validate email addresses, check delivery statistics,
      manage suppression lists, configure webhooks, and handle domain settings.
      UniOne delivers billions of emails annually with 99.88% deliverability.
    metadata:
      openclaw:
        emoji: "📧"
        requires:
          env:
            - UNIONE_API_KEY
        primaryEnv: UNIONE_API_KEY
    ---
    
    # UniOne Email API
    
    UniOne is a transactional email service with Web API for sending transactional and marketing emails at scale (up to 3,000 emails/sec). This skill lets you send emails, manage templates, validate addresses, track delivery, and more.
    
    ## Authentication
    
    All requests require the `UNIONE_API_KEY` environment variable. Pass it as the `X-API-KEY` header.
    
    **Base URL:** `https://api.unione.io/en/transactional/api/v1/{method}.json?platform=openclaw`
    
    All methods use `POST` with JSON body.
    
    ---
    
    ## CRITICAL: Domain Setup (Required Before Sending)
    
    **Emails will not be delivered until the sender's domain is verified.** Before attempting to send any email, ensure the domain is set up:
    
    ### Step 1: Get DNS Record Values — `domain/get-dns-records.json`
    
    ```bash
    curl -X POST "https://api.unione.io/en/transactional/api/v1/domain/get-dns-records.json?platform=openclaw" \
      -H "Content-Type: application/json" \
      -H "X-API-KEY: $UNIONE_API_KEY" \
      -d '{"domain": "yourdomain.com"}'
    ```
    
    **API response** returns raw values (not ready-to-paste DNS records):
    
    ```json
    {
      "status": "success",
      "domain": "yourdomain.com",
      "verification-record": "unione-validate-hash=483bb362ebdbeedd755cfb1d4d661",
      "dkim": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDo7"
    }
    ```
    
    **The user must create 3 DNS TXT records from these values:**
    
    | Record Host | Record Type | Value |
    |-------------|-------------|-------|
    | `@` | TXT | `unione-validate-hash=<verification-record from response>` |
    | `us._domainkey` | TXT | `k=rsa; p=<dkim from response>` |
    | `@` | TXT | `v=spf1 include:spf.unione.io ~all` |
    
    Present these 3 records clearly to the user and instruct them to add them at their DNS provider (Cloudflare, Route53, GoDaddy, etc.). The SPF record is always the same — it is not returned by the API.
    
    ### Step 2: Verify Domain Ownership — `domain/validate-verification.json`
    
    After the user has added DNS records:
    
    ```bash
    curl -X POST "https://api.unione.io/en/transactional/api/v1/domain/validate-verification.json?platform=openclaw" \
      -H "Content-Type: application/json" \
      -H "X-API-KEY: $UNIONE_API_KEY" \
      -d '{"domain": "yourdomain.com"}'
    ```
    
    ### Step 3: Validate DKIM — `domain/validate-dkim.json`
    
    ```bash
    curl -X POST "https://api.unione.io/en/transactional/api/v1/domain/validate-dkim.json?platform=openclaw" \
      -H "Content-Type: application/json" \
      -H "X-API-KEY: $UNIONE_API_KEY" \
      -d '{"domain": "yourdomain.com"}'
    ```
    
    ### Step 4: List All Domains — `domain/list.json`
    
    ```bash
    curl -X POST "https://api.unione.io/en/transactional/api/v1/domain/list.json?platform=openclaw" \
      -H "Content-Type: application/json" \
      -H "X-API-KEY: $UNIONE_API_KEY" \
      -d '{}'
    ```
    
    **If domain verification fails:** DNS propagation can take up to 48 hours. Suggest the user waits and retries, or checks their DNS records for typos.
    
    ---
    
    ## Error Handling & Retry Policy
    
    ### Retry Logic
    
    When making API requests, implement exponential backoff for retryable errors:
    
    **Retryable errors (DO retry with exponential backoff):**
    
    | HTTP Code | Meaning | Retry Strategy |
    |-----------|---------|----------------|
    | 429 | Rate limited | Wait, then retry. Respect `Retry-After` header if present |
    | 500 | Internal server error | Retry up to 3 times |
    | 502 | Bad gateway | Retry up to 3 times |
    | 503 | Service unavailable | Retry up to 3 times |
    | 504 | Gateway timeout | Retry up to 3 times |
    
    **Recommended retry schedule:**
    
    | Attempt | Delay |
    |---------|-------|
    | 1 | Immediate |
    | 2 | 1 second |
    | 3 | 5 seconds |
    | 4 | 30 seconds |
    
    **Non-retryable errors (do NOT retry):**
    
    | HTTP Code | Meaning | Action |
    |-----------|---------|--------|
    | 400 | Bad request | Fix the request parameters |
    | 401 | Unauthorized | Check API key |
    | 403 | Forbidden | Check permissions / domain verification |
    | 404 | Endpoint not found | Check the method path |
    | 413 | Payload too large | Reduce request size |
    
    ### Idempotency
    
    For `email/send.json`, always include an `idempotency_key` to prevent duplicate sends during retries. This is critical for production systems.
    
    The `idempotency_key` is a unique string (UUID recommended) passed in the request body. If UniOne receives two requests with the same key, the second request returns the result of the first without sending another email.
    
    **Always generate a unique idempotency key per logical send operation, and reuse the same key when retrying the same send.**
    
    ---
    
    ## 1. Send Email — `email/send.json`
    
    Send a transactional or marketing email to one or more recipients. Supports personalization via substitutions, templates, attachments, tracking, and metadata.
    
    ### curl
    
    ```bash
    curl -X POST "https://api.unione.io/en/transactional/api/v1/email/send.json?platform=openclaw" \
      -H "Content-Type: application/json" \
      -H "X-API-KEY: $UNIONE_API_KEY" \
      -d '{
        "idempotency_key": "unique-uuid-here",
        "message": {
          "recipients": [
            {
              "email": "recipient@example.com",
              "substitutions": {
                "to_name": "John Smith"
              }
            }
          ],
          "body": {
            "html": "<h1>Hello, {{to_name}}!</h1><p>Your order has been confirmed.</p>",
            "plaintext": "Hello, {{to_name}}! Your order has been confirmed."
          },
          "subject": "Order Confirmation",
          "from_email": "noreply@yourdomain.com",
          "from_name": "Your Store"
        }
      }'
    ```
    
    ### Node.js
    
    ```javascript
    const response = await fetch("https://api.unione.io/en/transactional/api/v1/email/send.json?platform=openclaw", {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "X-API-KEY": process.env.UNIONE_API_KEY
      },
      body: JSON.stringify({
        idempotency_key: crypto.randomUUID(),
        message: {
          recipients: [{ email: "recipient@example.com", substitutions: { to_name: "John" } }],
          body: {
            html: "<h1>Hello, {{to_name}}!</h1><p>Your order has been confirmed.</p>",
            plaintext: "Hello, {{to_name}}! Your order has been confirmed."
          },
          subject: "Order Confirmation",
          from_email: "noreply@yourdomain.com",
          from_name: "Your Store"
        }
      })
    });
    const data = await response.json();
    // data.status === "success" → data.job_id, data.emails
    ```
    
    ### Python
    
    ```python
    import requests, uuid, os
    
    response = requests.post(
        "https://api.unione.io/en/transactional/api/v1/email/send.json?platform=openclaw",
        headers={
            "Content-Type": "application/json",
            "X-API-KEY": os.environ["UNIONE_API_KEY"]
        },
        json={
            "idempotency_key": str(uuid.uuid4()),
            "message": {
                "recipients": [{"email": "recipient@example.com", "substitutions": {"to_name": "John"}}],
                "body": {
                    "html": "<h1>Hello, {{to_name}}!</h1><p>Your order has been confirmed.</p>",
                    "plaintext": "Hello, {{to_name}}! Your order has been confirmed."
                },
                "subject": "Order Confirmation",
                "from_email": "noreply@yourdomain.com",
                "from_name": "Your Store"
            }
        }
    )
    data = response.json()  # data["status"] == "success" → data["job_id"], data["emails"]
    ```
    
    ### Go
    
    ```go
    package main
    
    import (
        "bytes"
        "encoding/json"
        "fmt"
        "net/http"
        "os"
        "github.com/google/uuid"
    )
    
    func sendEmail() error {
        payload := map[string]interface{}{
            "idempotency_key": uuid.New().String(),
            "message": map[string]interface{}{
                "recipients": []map[string]interface{}{
                    {"email": "recipient@example.com", "substitutions": map[string]string{"to_name": "John"}},
                },
                "body": map[string]string{
                    "html":      "<h1>Hello, {{to_name}}!</h1><p>Your order has been confirmed.</p>",
                    "plaintext": "Hello, {{to_name}}! Your order has been confirmed.",
                },
                "subject":    "Order Confirmation",
                "from_email": "noreply@yourdomain.com",
                "from_name":  "Your Store",
            },
        }
        body, _ := json.Marshal(payload)
        req, _ := http.NewRequest("POST",
            "https://api.unione.io/en/transactional/api/v1/email/send.json?platform=openclaw",
            bytes.NewReader(body))
        req.Header.Set("Content-Type", "application/json")
        req.Header.Set("X-API-KEY", os.Getenv("UNIONE_API_KEY"))
        resp, err := http.DefaultClient.Do(req)
        if err != nil {
            return err
        }
        defer resp.Body.Close()
        var result map[string]interface{}
        json.NewDecoder(resp.Body).Decode(&result)
        fmt.Println(result) // result["status"] == "success"
        return nil
    }
    ```
    
    ### PHP
    
    ```php
    $ch = curl_init("https://api.unione.io/en/transactional/api/v1/email/send.json?platform=openclaw");
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_HTTPHEADER => [
            "Content-Type: application/json",
            "X-API-KEY: " . getenv("UNIONE_API_KEY")
        ],
        CURLOPT_POSTFIELDS => json_encode([
            "idempotency_key" => bin2hex(random_bytes(16)),
            "message" => [
                "recipients" => [["email" => "recipient@example.com", "substitutions" => ["to_name" => "John"]]],
                "body" => [
                    "html" => "<h1>Hello, {{to_name}}!</h1><p>Your order has been confirmed.</p>",
                    "plaintext" => "Hello, {{to_name}}! Your order has been confirmed."
                ],
                "subject" => "Order Confirmation",
                "from_email" => "noreply@yourdomain.com",
                "from_name" => "Your Store"
            ]
        ])
    ]);
    $response = curl_exec($ch);
    $data =
    
    ... (truncated)