Démarrage
Utiliser depuis n'importe quel langage
L'API REST, c'est du HTTPS + JSON pur. N'importe quel langage avec un client HTTP peut envoyer des events. Ci-dessous, le même appel POST /v1/events écrit de manière idiomatique dans plusieurs langages. Insère ta clé API, lance, terminé.
Chaque exemple cible :
text
POST https://api.recalled.dev/v1/events
Authorization: Bearer $RECALLED_API_KEY
Content-Type: application/jsonAvec le body :
json
{
"action": "invoice.deleted",
"actor": { "id": "user_123", "email": "alice@example.com" },
"organization": "org_acme",
"targets": [{ "type": "invoice", "id": "inv_42" }],
"metadata": { "reason": "duplicate" }
}curl
bash
curl -X POST https://api.recalled.dev/v1/events \
-H "Authorization: Bearer $RECALLED_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"action": "invoice.deleted",
"actor": { "id": "user_123", "email": "alice@example.com" },
"organization": "org_acme",
"targets": [{ "type": "invoice", "id": "inv_42" }],
"metadata": { "reason": "duplicate" }
}'Node.js (fetch, sans SDK)
Si tu ne veux pas du SDK et préfères un fetch brut :
js
const response = await fetch("https://api.recalled.dev/v1/events", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.RECALLED_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
action: "invoice.deleted",
actor: { id: "user_123", email: "alice@example.com" },
organization: "org_acme",
targets: [{ type: "invoice", id: "inv_42" }],
metadata: { reason: "duplicate" },
}),
});
if (!response.ok) throw new Error(await response.text());
const { data: event } = await response.json();Python (requests)
python
import os
import requests
response = requests.post(
"https://api.recalled.dev/v1/events",
headers={
"Authorization": f"Bearer {os.environ['RECALLED_API_KEY']}",
"Content-Type": "application/json",
},
json={
"action": "invoice.deleted",
"actor": {"id": "user_123", "email": "alice@example.com"},
"organization": "org_acme",
"targets": [{"type": "invoice", "id": "inv_42"}],
"metadata": {"reason": "duplicate"},
},
timeout=10,
)
response.raise_for_status()
event = response.json()["data"]Go (net/http)
go
package main
import (
"bytes"
"encoding/json"
"net/http"
"os"
"time"
)
func main() {
body, _ := json.Marshal(map[string]any{
"action": "invoice.deleted",
"actor": map[string]any{"id": "user_123", "email": "alice@example.com"},
"organization": "org_acme",
"targets": []map[string]any{{"type": "invoice", "id": "inv_42"}},
"metadata": map[string]any{"reason": "duplicate"},
})
req, _ := http.NewRequest("POST", "https://api.recalled.dev/v1/events", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("RECALLED_API_KEY"))
req.Header.Set("Content-Type", "application/json")
client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
}PHP (curl)
php
<?php
$body = json_encode([
"action" => "invoice.deleted",
"actor" => ["id" => "user_123", "email" => "alice@example.com"],
"organization" => "org_acme",
"targets" => [["type" => "invoice", "id" => "inv_42"]],
"metadata" => ["reason" => "duplicate"],
]);
$ch = curl_init("https://api.recalled.dev/v1/events");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $body,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: Bearer " . getenv("RECALLED_API_KEY"),
"Content-Type: application/json",
],
]);
$response = curl_exec($ch);
curl_close($ch);Ruby (Net::HTTP)
ruby
require "net/http"
require "json"
require "uri"
uri = URI("https://api.recalled.dev/v1/events")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer #{ENV['RECALLED_API_KEY']}"
request["Content-Type"] = "application/json"
request.body = {
action: "invoice.deleted",
actor: { id: "user_123", email: "alice@example.com" },
organization: "org_acme",
targets: [{ type: "invoice", id: "inv_42" }],
metadata: { reason: "duplicate" },
}.to_json
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
http.request(request)
endJava (java.net.http)
java
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
String body = """
{
"action": "invoice.deleted",
"actor": { "id": "user_123", "email": "alice@example.com" },
"organization": "org_acme",
"targets": [{ "type": "invoice", "id": "inv_42" }],
"metadata": { "reason": "duplicate" }
}
""";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.recalled.dev/v1/events"))
.header("Authorization", "Bearer " + System.getenv("RECALLED_API_KEY"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());Rust (reqwest)
rust
use serde_json::json;
let client = reqwest::Client::new();
let response = client
.post("https://api.recalled.dev/v1/events")
.header(
"Authorization",
format!("Bearer {}", std::env::var("RECALLED_API_KEY")?),
)
.header("Content-Type", "application/json")
.json(&json!({
"action": "invoice.deleted",
"actor": { "id": "user_123", "email": "alice@example.com" },
"organization": "org_acme",
"targets": [{ "type": "invoice", "id": "inv_42" }],
"metadata": { "reason": "duplicate" }
}))
.send()
.await?
.error_for_status()?;Gestion des erreurs
Toutes les réponses non-2xx ont la même forme :
json
{
"error": {
"code": "PLAN_LIMIT_REACHED",
"message": "Monthly event quota exceeded",
"details": { "limit": 5000 }
}
}400:VALIDATION_ERROR, le body a échoué la validation (les champs fautifs sont dansdetails)401:UNAUTHORIZED,INVALID_API_KEYouREVOKED_API_KEY, clé manquante, invalide ou révoquée403:FORBIDDEN, la clé est valide mais la feature est gated par le plan429:RATE_LIMITED(regardeRateLimit-Reset) ouPLAN_LIMIT_REACHED(quota mensuel)5xx: retry avec backoff
Liste complète dans Codes d'erreur.
Stratégie de retry
Retry sur 408, 429, 502, 503, 504 avec backoff exponentiel (1s à 10min). Pas de retry sur 400, 401, 403, 404, ce sont des erreurs permanentes.
Le SDK npm implémente tout ça pour toi, y compris une queue en mémoire de 24 h pour emit(). Si tu es sur Node, utilise le SDK. Sinon, enrobe ton client HTTP dans une boucle de retry.