Docs / Utvecklare

REST API

Använd tenant-API-nycklar från ert CMS eller backend. Dashboard-sessioncookies är bara för EngageNudge-appen. Anropa API:t från er server. Lägg aldrig nyckeln i webbläsaren.

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

Skapa en API-nyckel

  1. Dashboard → Integrationer → REST API
  2. Som tenant-ägare, skapa en nyckel och kopiera hemligheten en gång (en_…)
  3. Spara den i er secret manager. Den visas aldrig igen.

Nyckelhantering (dashboard-session / ägare 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

Nyckelns tenant måste matcha kampanjens tenant. Logga inte rånyckeln. Ni behöver också tenant-id och sajt-id (UUID) från install-snippeten.

Rekommenderat flöde

(valfritt) POST /v1/content/preview
           POST /v1/tenants/:tenantId/campaigns   → campaignId (draft)
           POST /v1/campaigns/:campaignId/test    → bara testenheter
       eller POST /v1/campaigns/:campaignId/send    → live-publik
       eller POST /v1/campaigns/:campaignId/schedule → scheduleAt (ISO-8601)

Live-utskick kräver accepterad DPA, att utskick inte är pausade, att billing tillåter publiken, och en skickbar kampanjstatus (oftast draft). Test kräver minst en testprenumerant.

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 krävs (HTTPS, SSRF-säker hämtning). siteId och tenantId gör beslutet publikmedvetet.

Skapa kampanj

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"
  }'
FältKrävsAnteckning
siteIdjaUUID på tenant
urljaDestinations- / artikel-URL
modenej"url" eller "manual"
title / bodymanualMax 120 / 240 tecken
imageUrlnejHTTPS-URL, eller "" för att rensa
topicIdsnejTopic-UUID-filter
forceSendnejURL-läge: skapa skickbart utkast även vid skip. RSS auto-push sätter det bara för ett flöde med åsidosättning påslagen.

Utan forceSend kan en avvisad artikel landa som cancelled. Dashboard-komponering skickar forceSend: true så redaktörer kan publicera.

JSON-svaret innehåller campaignId. Använd det id:t för test, skicka eller schemalägg.

Exempel

Skapa en kampanj och skicka den. Samma sökväg och headers i varje språk. Läs API_KEY, TENANT_ID och SITE_ID från er miljö. Hårdkoda inte hemligheten.

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, skicka, schemalägg

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 måste vara ISO-8601 i framtiden. Idempotent replay returnerar "duplicate": true.

Vanliga fel

StatusKodBetydelse
401-Saknad eller ogiltig API-nyckel
403dpa_requiredAcceptera DPA i Inställningar
402billingPlan eller publikkap blockerad
409illegal_transitionKampanjen är inte skickbar
423send_pausedPaus på sajt eller tenant

Stoppa en pågående körning från dashboarden eller POST /v1/runs/:runId/stop.

Inte i det här API:t än

  • Publicerad OpenAPI
  • Utgående webhooks
  • Live CMS HMAC-ingest (dashboarden visar bara avsedd payload)
  • Subscriber CRUD bortom utskicksvägen

Relaterat