EngageNudge

Docs / Desenvolvedores

Este artigo é exibido em inglês.

REST API

Use tenant API keys from your CMS or backend. Dashboard session cookies are for the EngageNudge app only. Call the API from your server, never put the key in the browser.

Base URLhttps://api.engagenudge.com
AuthAuthorization: Bearer en_…
Content typeapplication/json

Create an API key

  1. Dashboard → Integrations → REST API
  2. As a tenant owner, create a key and copy the secret once (en_…)
  3. Store it in your secret manager. It is never shown again.

Key management (dashboard session / owner only):

MethodPath
GET/v1/tenants/:tenantId/api-keys
POST/v1/tenants/:tenantId/api-keys body { "name": "CMS production" }
DELETE/v1/tenants/:tenantId/api-keys/:keyId

Auth

HTTP
Authorization: Bearer en_YOUR_API_KEY

The key’s tenant must match the campaign tenant. Do not log the raw key. You also need tenant id and site id (UUIDs) from the install snippet.

(optional) POST /v1/content/preview
           POST /v1/tenants/:tenantId/campaigns   → campaignId (draft)
           POST /v1/campaigns/:campaignId/test    → test devices only
       or  POST /v1/campaigns/:campaignId/send    → live audience
       or  POST /v1/campaigns/:campaignId/schedule → scheduleAt (ISO-8601)

Live send requires DPA accepted, sends not paused, billing allowing the audience, and a sendable campaign status (usually draft). Test send requires at least one test subscriber.

Preview

cURL
curl -X POST "https://api.engagenudge.com/v1/content/preview" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $API_KEY" \
  -d '{
    "url": "https://publisher.example/story",
    "siteId": "'"$SITE_ID"'",
    "tenantId": "'"$TENANT_ID"'"
  }'

url is required (HTTPS, SSRF-safe fetch). siteId and tenantId make the decision audience-aware.

Create campaign

cURL
curl -X POST "https://api.engagenudge.com/v1/tenants/$TENANT_ID/campaigns" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $API_KEY" \
  -d '{
    "siteId": "'"$SITE_ID"'",
    "url": "https://publisher.example/story",
    "imageUrl": "https://publisher.example/image.jpg"
  }'
FieldRequiredNotes
siteIdyesUUID on the tenant
urlyesDestination / article URL
modeno"url" or "manual"
title / bodymanualMax 120 / 240 chars
imageUrlnoHTTPS URL, or "" to clear
topicIdsnoTopic UUID filter
forceSendnoURL mode: create a sendable draft even when the decision is skip. RSS auto-push sets it only for a feed with the override turned on.

Without forceSend, a rejected story may land as cancelled. Dashboard compose sends forceSend: true so editors can ship.

The JSON response includes campaignId. Use that id for test, send, or schedule.

Examples

Create a campaign, then send it. Same path and headers in every language. Read API_KEY, TENANT_ID, and SITE_ID from your environment. Do not hard-code the secret.

JavaScript

JavaScript
const API_URL = "https://api.engagenudge.com";
const API_KEY = process.env.ENGAGENUDGE_API_KEY;
const TENANT_ID = process.env.ENGAGENUDGE_TENANT_ID;
const SITE_ID = process.env.ENGAGENUDGE_SITE_ID;

const created = await fetch(`${API_URL}/v1/tenants/${TENANT_ID}/campaigns`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    siteId: SITE_ID,
    url: "https://publisher.example/story",
    imageUrl: "https://publisher.example/image.jpg",
  }),
});

if (!created.ok) {
  throw new Error(`Create failed: ${created.status} ${await created.text()}`);
}

const { campaignId } = await created.json();

const sent = await fetch(`${API_URL}/v1/campaigns/${campaignId}/send`, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${API_KEY}`,
    "Idempotency-Key": crypto.randomUUID(),
  },
});

if (!sent.ok) {
  throw new Error(`Send failed: ${sent.status} ${await sent.text()}`);
}

TypeScript

TypeScript
const API_URL = "https://api.engagenudge.com";

type CampaignCreated = { campaignId: string };

async function createAndSendNudge(input: {
  apiKey: string;
  tenantId: string;
  siteId: string;
  url: string;
  imageUrl?: string;
}): Promise<CampaignCreated> {
  const created = await fetch(`${API_URL}/v1/tenants/${input.tenantId}/campaigns`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${input.apiKey}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      siteId: input.siteId,
      url: input.url,
      imageUrl: input.imageUrl,
    }),
  });

  if (!created.ok) {
    throw new Error(`Create failed: ${created.status} ${await created.text()}`);
  }

  const campaign = (await created.json()) as CampaignCreated;

  const sent = await fetch(`${API_URL}/v1/campaigns/${campaign.campaignId}/send`, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${input.apiKey}`,
      "Idempotency-Key": crypto.randomUUID(),
    },
  });

  if (!sent.ok) {
    throw new Error(`Send failed: ${sent.status} ${await sent.text()}`);
  }

  return campaign;
}

Python

Python
import json
import os
import uuid
import urllib.error
import urllib.request

API_URL = "https://api.engagenudge.com"
API_KEY = os.environ["ENGAGENUDGE_API_KEY"]
TENANT_ID = os.environ["ENGAGENUDGE_TENANT_ID"]
SITE_ID = os.environ["ENGAGENUDGE_SITE_ID"]


def request(method: str, path: str, payload: dict | None = None, extra_headers: dict | None = None) -> dict:
    headers = {
        "Authorization": f"Bearer {API_KEY}",
        "Content-Type": "application/json",
        **(extra_headers or {}),
    }
    data = None if payload is None else json.dumps(payload).encode()
    req = urllib.request.Request(f"{API_URL}{path}", data=data, headers=headers, method=method)
    try:
        with urllib.request.urlopen(req) as res:
            body = res.read().decode()
            return json.loads(body) if body else {}
    except urllib.error.HTTPError as exc:
        raise RuntimeError(f"{method} {path} failed: {exc.code} {exc.read().decode()}") from exc


campaign = request(
    "POST",
    f"/v1/tenants/{TENANT_ID}/campaigns",
    {
        "siteId": SITE_ID,
        "url": "https://publisher.example/story",
        "imageUrl": "https://publisher.example/image.jpg",
    },
)
request(
    "POST",
    f"/v1/campaigns/{campaign['campaignId']}/send",
    extra_headers={"Idempotency-Key": str(uuid.uuid4())},
)

PHP

PHP
<?php
$apiUrl = 'https://api.engagenudge.com';
$apiKey = getenv('ENGAGENUDGE_API_KEY');
$tenantId = getenv('ENGAGENUDGE_TENANT_ID');
$siteId = getenv('ENGAGENUDGE_SITE_ID');

function en_request(string $method, string $url, string $apiKey, ?array $payload = null, array $extraHeaders = []): array {
  $headers = array_merge([
    "Authorization: Bearer $apiKey",
    'Content-Type: application/json',
  ], $extraHeaders);

  $ch = curl_init($url);
  curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST => $method,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_HTTPHEADER => $headers,
    CURLOPT_POSTFIELDS => $payload === null ? null : json_encode($payload),
  ]);

  $body = curl_exec($ch);
  $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  curl_close($ch);

  if ($status < 200 || $status >= 300) {
    throw new RuntimeException("$method $url failed: $status $body");
  }

  return $body ? json_decode($body, true) : [];
}

$campaign = en_request('POST', "$apiUrl/v1/tenants/$tenantId/campaigns", $apiKey, [
  'siteId' => $siteId,
  'url' => 'https://publisher.example/story',
  'imageUrl' => 'https://publisher.example/image.jpg',
]);

en_request(
  'POST',
  "$apiUrl/v1/campaigns/{$campaign['campaignId']}/send",
  $apiKey,
  null,
  ['Idempotency-Key: ' . bin2hex(random_bytes(16))]
);

Java

Java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.UUID;

static String jsonField(String json, String field) {
    String key = "\"" + field + "\"";
    int at = json.indexOf(key);
    if (at < 0) throw new IllegalStateException(field + " missing: " + json);
    int colon = json.indexOf(':', at + key.length());
    int start = json.indexOf('"', colon + 1) + 1;
    return json.substring(start, json.indexOf('"', start));
}

String apiUrl = "https://api.engagenudge.com";
String apiKey = System.getenv("ENGAGENUDGE_API_KEY");
String tenantId = System.getenv("ENGAGENUDGE_TENANT_ID");
String siteId = System.getenv("ENGAGENUDGE_SITE_ID");

HttpClient client = HttpClient.newHttpClient();

String createJson = """
    {
      "siteId": "%s",
      "url": "https://publisher.example/story",
      "imageUrl": "https://publisher.example/image.jpg"
    }
    """.formatted(siteId);

HttpResponse<String> created = client.send(
    HttpRequest.newBuilder()
        .uri(URI.create(apiUrl + "/v1/tenants/" + tenantId + "/campaigns"))
        .header("Authorization", "Bearer " + apiKey)
        .header("Content-Type", "application/json")
        .POST(HttpRequest.BodyPublishers.ofString(createJson))
        .build(),
    HttpResponse.BodyHandlers.ofString());

if (created.statusCode() < 200 || created.statusCode() >= 300) {
    throw new IllegalStateException("Create failed: " + created.statusCode() + " " + created.body());
}

String campaignId = jsonField(created.body(), "campaignId");

HttpResponse<String> sent = client.send(
    HttpRequest.newBuilder()
        .uri(URI.create(apiUrl + "/v1/campaigns/" + campaignId + "/send"))
        .header("Authorization", "Bearer " + apiKey)
        .header("Idempotency-Key", UUID.randomUUID().toString())
        .POST(HttpRequest.BodyPublishers.noBody())
        .build(),
    HttpResponse.BodyHandlers.ofString());

if (sent.statusCode() < 200 || sent.statusCode() >= 300) {
    throw new IllegalStateException("Send failed: " + sent.statusCode() + " " + sent.body());
}

C# (.NET)

C#
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;

const string apiUrl = "https://api.engagenudge.com";
var apiKey = Environment.GetEnvironmentVariable("ENGAGENUDGE_API_KEY");
var tenantId = Environment.GetEnvironmentVariable("ENGAGENUDGE_TENANT_ID");
var siteId = Environment.GetEnvironmentVariable("ENGAGENUDGE_SITE_ID");

using var client = new HttpClient { BaseAddress = new Uri(apiUrl) };
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey);

var created = await client.PostAsJsonAsync(
    $"/v1/tenants/{tenantId}/campaigns",
    new
    {
        siteId,
        url = "https://publisher.example/story",
        imageUrl = "https://publisher.example/image.jpg",
    });
created.EnsureSuccessStatusCode();

using var createdJson = JsonDocument.Parse(await created.Content.ReadAsStringAsync());
var campaignId = createdJson.RootElement.GetProperty("campaignId").GetString();

using var sendRequest = new HttpRequestMessage(HttpMethod.Post, $"/v1/campaigns/{campaignId}/send");
sendRequest.Headers.TryAddWithoutValidation("Idempotency-Key", Guid.NewGuid().ToString());
var sent = await client.SendAsync(sendRequest);
sent.EnsureSuccessStatusCode();

Test, send, schedule

cURL
curl -X POST "https://api.engagenudge.com/v1/campaigns/$CAMPAIGN_ID/test" \
  -H "Authorization: Bearer $API_KEY"

curl -X POST "https://api.engagenudge.com/v1/campaigns/$CAMPAIGN_ID/send" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Idempotency-Key: optional-unique-key"

curl -X POST "https://api.engagenudge.com/v1/campaigns/$CAMPAIGN_ID/schedule" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $API_KEY" \
  -d '{ "scheduleAt": "2026-08-10T09:00:00.000Z" }'

scheduleAt must be ISO-8601 in the future. Idempotent send replay returns "duplicate": true.

Common errors

StatusCodeMeaning
401-Missing or invalid API key
403dpa_requiredAccept the DPA in Settings
402billingPlan or audience cap blocked
409illegal_transitionCampaign not sendable
423send_pausedSite or tenant pause is on

Stop an in-flight run from the dashboard or POST /v1/runs/:runId/stop.

Not in this API yet

  • Published OpenAPI
  • Outbound webhooks
  • Live CMS HMAC ingest (the dashboard shows the intended payload only)
  • Subscriber CRUD beyond the send path