Partner API v1 · Event catalog v2
Partner Event API
Send one destination business event to MeetROAS. Every request is signed with account credentials and carries the mr_click_id received when the visitor reached the destination.
1. Obtain a Key ID and Secret
- A MeetROAS customer account Admin signs in to app.meetroas.com.
- Open Conversions → Destination partner API.
- Select Create credential. The dashboard displays a Key ID and Secret.
- Store both values immediately in the destination platform's secrets manager. The Secret cannot be read again after leaving the page.
No MeetROAS support or partner contact is required. The customer account Admin creates the credential and sends it to the destination engineering team through a secure channel; rotate it from the same page if lost.
2. Receive and persist mr_click_id
MeetROAS appends mr_click_id to the destination URL. Read it on the landing request and persist it with the server-side visitor or transaction record.
https://casino.example/register?mr_click_id=mrc_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx- Do not parse, modify, or mint this value.
- Do not use the Key ID or Secret as mr_click_id.
- mr_click_id remains valid for 30 days; unknown, expired, or account-mismatched IDs return 404.
3. Build headers and the HMAC signature
| Header | Value | Produced by |
|---|---|---|
Content-Type | application/json | Fixed by the destination |
X-MeetROAS-Key | mrk_… | Created by the MeetROAS account Admin |
X-MeetROAS-Timestamp | Current 10-digit Unix seconds | Generated for every request |
X-MeetROAS-Signature | v1=<64 lowercase hex> | Computed with the Secret; it is not a separate credential |
- Serialize the final JSON raw body first.
- Set timestamp = floor(current_time_ms / 1000).
- The signing input is timestamp + '.' + raw_body.
- Compute HMAC-SHA256 with the Secret, encode lowercase hex, then prefix v1=.
1786344000.{"event_id":"payment-123",...}Send the exact body that was signed. Re-indenting, reordering fields, or adding a newline invalidates the signature. Server time must be within five minutes of UTC.
4. Validate the integration
POST https://api.meetroas.com/v1/events/validate
The validation endpoint applies the same credentials, headers, signature, and schema as production without creating a production event or triggering a traffic-platform conversion.
Choose your server language
Each example serializes the body once, signs those exact bytes, and sends them to the validation endpoint without creating a production event.
Node.js 20+
import { createHmac, randomUUID } from "node:crypto";
const keyId = process.env.MEETROAS_KEY_ID;
const secret = process.env.MEETROAS_SECRET;
if (!keyId || !secret) throw new Error("Missing MeetROAS credentials");
const body = JSON.stringify({
event_id: `test-${randomUUID()}`,
mr_click_id: "mrc_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
event: "purchase",
occurred_at: new Date().toISOString(),
value: 49.95,
currency: "USD",
});
const timestamp = String(Math.floor(Date.now() / 1000));
const digest = createHmac("sha256", secret)
.update(`${timestamp}.${body}`, "utf8")
.digest("hex");
const response = await fetch("https://api.meetroas.com/v1/events/validate", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-MeetROAS-Key": keyId,
"X-MeetROAS-Timestamp": timestamp,
"X-MeetROAS-Signature": `v1=${digest}`,
},
body,
});
console.log(response.status, await response.text());Java 11+
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.UUID;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class MeetROASExample {
public static void main(String[] args) throws Exception {
String keyId = requireEnv("MEETROAS_KEY_ID");
String secret = requireEnv("MEETROAS_SECRET");
String body = String.format(
"{\"event_id\":\"test-%s\",\"mr_click_id\":\"mrc_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\"," +
"\"event\":\"purchase\",\"occurred_at\":\"%s\",\"value\":49.95,\"currency\":\"USD\"}",
UUID.randomUUID(), Instant.now());
String timestamp = Long.toString(Instant.now().getEpochSecond());
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
byte[] digest = mac.doFinal((timestamp + "." + body).getBytes(StandardCharsets.UTF_8));
StringBuilder hex = new StringBuilder();
for (byte value : digest) hex.append(String.format("%02x", value & 0xff));
HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.meetroas.com/v1/events/validate"))
.header("Content-Type", "application/json")
.header("X-MeetROAS-Key", keyId)
.header("X-MeetROAS-Timestamp", timestamp)
.header("X-MeetROAS-Signature", "v1=" + hex)
.POST(HttpRequest.BodyPublishers.ofString(body, StandardCharsets.UTF_8)).build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.statusCode() + " " + response.body());
}
private static String requireEnv(String name) {
String value = System.getenv(name);
if (value == null || value.isBlank()) throw new IllegalStateException("Missing " + name);
return value;
}
}Go 1.22+
package main
import (
"bytes"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strconv"
"time"
)
type event struct {
EventID string `json:"event_id"`
ClickID string `json:"mr_click_id"`
Event string `json:"event"`
OccurredAt string `json:"occurred_at"`
Value float64 `json:"value"`
Currency string `json:"currency"`
}
func main() {
keyID, secret := os.Getenv("MEETROAS_KEY_ID"), os.Getenv("MEETROAS_SECRET")
if keyID == "" || secret == "" { panic("missing MeetROAS credentials") }
body, err := json.Marshal(event{
"test-" + strconv.FormatInt(time.Now().UnixNano(), 10), "mrc_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"purchase", time.Now().UTC().Format(time.RFC3339), 49.95, "USD",
})
if err != nil { panic(err) }
timestamp := strconv.FormatInt(time.Now().Unix(), 10)
mac := hmac.New(sha256.New, []byte(secret))
mac.Write(append([]byte(timestamp+"."), body...))
req, err := http.NewRequest("POST", "https://api.meetroas.com/v1/events/validate", bytes.NewReader(body))
if err != nil { panic(err) }
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-MeetROAS-Key", keyID)
req.Header.Set("X-MeetROAS-Timestamp", timestamp)
req.Header.Set("X-MeetROAS-Signature", "v1="+hex.EncodeToString(mac.Sum(nil)))
response, err := http.DefaultClient.Do(req)
if err != nil { panic(err) }
defer response.Body.Close()
responseBody, _ := io.ReadAll(response.Body)
fmt.Printf("%d %s\n", response.StatusCode, responseBody)
}C# · .NET 8+
using System.Security.Cryptography;
using System.Text;
using System.Text.Json;
var keyId = RequireEnv("MEETROAS_KEY_ID");
var secret = RequireEnv("MEETROAS_SECRET");
var body = JsonSerializer.Serialize(new {
event_id = $"test-{Guid.NewGuid()}",
mr_click_id = "mrc_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
@event = "purchase",
occurred_at = DateTimeOffset.UtcNow.ToString("O"),
value = 49.95,
currency = "USD",
});
var timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
using var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(secret));
var digest = hmac.ComputeHash(Encoding.UTF8.GetBytes($"{timestamp}.{body}"));
using var request = new HttpRequestMessage(HttpMethod.Post, "https://api.meetroas.com/v1/events/validate");
request.Content = new StringContent(body, Encoding.UTF8, "application/json");
request.Headers.Add("X-MeetROAS-Key", keyId);
request.Headers.Add("X-MeetROAS-Timestamp", timestamp);
request.Headers.Add("X-MeetROAS-Signature", $"v1={Convert.ToHexString(digest).ToLowerInvariant()}");
using var response = await new HttpClient().SendAsync(request);
Console.WriteLine($"{(int)response.StatusCode} {await response.Content.ReadAsStringAsync()}");
static string RequireEnv(string name) => Environment.GetEnvironmentVariable(name)
?? throw new InvalidOperationException($"Missing {name}");{
"valid": true,
"authentication": "valid",
"schema": "valid",
"event": "purchase",
"catalog_version": 2
}The customer account Admin can also select Validate signature and schema immediately after creating the credential in MeetROAS.
5. Send production events
POST https://api.meetroas.com/v1/events
{
"event_id": "test-9c8d0b15-4c87-49db-8c6e-6fcb80e0e39f",
"mr_click_id": "mrc_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
"event": "purchase",
"occurred_at": "2026-08-10T17:01:59.042Z",
"value": 49.95,
"currency": "USD"
}| Field | Required | Format and semantics |
|---|---|---|
event_id | Yes | Destination-generated idempotency key, 1–128 characters, no personal data; preserve it for retries of the same business fact. |
mr_click_id | Yes | Opaque MeetROAS ID persisted from the destination URL. |
event | Yes | Must be present in the accepted event catalog below. |
occurred_at | Yes | ISO-8601 UTC; event time must be within 31 days. |
value | Optional for monetary events | Decimal amount from 0 to 1,000,000,000 with up to six decimals; not minor units and never converted by MeetROAS. |
currency | Optional | Uppercase ISO 4217 code; requires value. MeetROAS never guesses a currency. |
Reusing an event_id with a different event, occurred_at, mr_click_id, value, or currency returns 409.
6. Accepted events
The event field must use one of these keys. Use the machine-readable catalog as the authoritative source for automated validation.
| Event key | When to send | Repeatability | event_id policy | Money |
|---|---|---|---|---|
account_registrationAccount registration | The user completed the destination platform's account registration flow; a page view or incomplete form does not qualify. | once_per_click | Use one stable ID for the registration and preserve it across retries. | Forbidden |
first_depositFirst deposit | The account's first verified deposit became effective; pending or failed transactions do not qualify. | once_per_click | Send once per account and preserve the same ID across retries. | Optional |
depositDeposit | A verified deposit became effective; this event is repeatable. | repeatable | Use a distinct ID per transaction and preserve it when retrying that transaction. | Optional |
purchasePurchase | A payment or purchase was verified and settled successfully; pending, failed, or refunded transactions do not qualify. | repeatable | Use a distinct ID per transaction and preserve it when retrying that transaction. | Optional |
7. Supported traffic platforms
The destination sends the same Partner Event API schema for every supported traffic platform and does not implement platform-specific postbacks.
| Status | Traffic platforms | Notes |
|---|---|---|
| Supported | TrafficStars, PropellerAds, RichAds, ExoClick | The customer configures the matching traffic-platform account and conversion goals in MeetROAS. |
| Planned | Adsterra, MGID, BidVertiser, HilltopAds, GeeMee, MGSkyAds | Not yet available as a supported production capability; do not plan a launch date around it. |
8. Responses, retries, and go-live
| HTTP | Meaning | Action |
|---|---|---|
200 | Validation passed, or a production event is an already processed duplicate | Success; do not mint a new event_id for the retry |
202 | Production event accepted for the first time | Success; do not retry |
400/415 | Invalid JSON, field, or Content-Type | Fix before retrying |
401 | Invalid Key, timestamp, or signature | Check credentials, server time, and the exact raw body |
404 | Production endpoint cannot resolve mr_click_id | Confirm the real redirect parameter was persisted and has not expired |
409 | event_id already identifies different business facts | Stop automatic retries and investigate idempotency |
429/5xx | Temporary limit or service error | Exponential backoff with jitter; preserve event_id and body |
- Obtain credentials and pass the validation endpoint.
- Send one production event using a real mr_click_id from a test visitor.
- Ask the customer account Admin to confirm the event in MeetROAS and complete traffic-platform configuration.
- After confirmation, begin sending production business events.
9. Security requirements
- Store the Secret only in a server-side secrets manager.
- Never put the Secret in a URL, browser code, support ticket, or application log.
- Use a fresh timestamp for every request and sign the exact raw body that will be sent.
- If credentials are lost or suspected to be exposed, the customer account Admin rotates them immediately. The old Key stops working at once.