Aller au contenu principal

🔧 Référence API — Edge Functions v2


Base URL & Authentification

Base URL : https://<SUPABASE_PROJECT_ID>.supabase.co/functions/v1/

Header requis :

Authorization: Bearer <SUPABASE_ANON_KEY>
Content-Type: application/json

ℹ️ verify_jwt = false en configuration actuelle (MVP). Migration vers JWT obligatoire prévue Q2 2026.


1. analyze-email-headers

Méthode : POST
Réponse : Server-Sent Events (SSE streaming)
Content-Type réponse : text/event-stream

Requête

{
"emailHeaders": "Delivered-To: user@gmail.com\nReceived: from...\n...",
"enabledApis": {
"ipQualityScore": true,
"abuseIPDB": false,
"virusTotal": false
},
"apiKeys": {
"ipQualityScore": "YOUR_KEY",
"abuseIPDB": "",
"virusTotal": ""
}
}

Events SSE émis

Format de chaque event : data: <JSON>\n\n

interface AnalysisStep {
step: string;
status: 'processing' | 'success' | 'warning' | 'error';
data: { message: string; [key: string]: any };
progress: number; // 0-100
}
StepProgressDonnées clés
basic_parsing10%from, subject, date, domains, ipv4
ip_parsing20%ipv4[], ipv6[]
received_chain30%chain[], tls, cipher
domain_arc40%domain, arcSeal, arcAuth
ip_reputation55%reputations[] (IPQS, AbuseIPDB, VirusTotal)
spf73%status: pass/fail/neutral
dkim85%status: pass/fail/neutral
complete100%summary complet

Suivi de data: [DONE] pour fin du stream.

Consommation depuis le frontend

const response = await fetch(
`${import.meta.env.VITE_SUPABASE_URL}/functions/v1/analyze-email-headers`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${import.meta.env.VITE_SUPABASE_PUBLISHABLE_KEY}`,
},
body: JSON.stringify({ emailHeaders, enabledApis, apiKeys })
}
);

const reader = response.body?.getReader();
const decoder = new TextDecoder();
let buffer = '';

while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
const lines = buffer.split('\n\n');
buffer = lines.pop() || '';
for (const line of lines) {
if (line.startsWith('data: ') && !line.includes('[DONE]')) {
const step = JSON.parse(line.slice(6));
// traiter step...
}
}
}

2. analyze-spam

Méthode : POST
Réponse : JSON synchrone

Requête

{
"emailContent": "Contenu de l'email...",
"subject": "Votre colis est prêt",
"from": "dpd@thepiratebuy.com",
"headers": "Received: from...",
"spf": "fail",
"dkim": "pass",
"dmarc": "fail",
"ipBlacklisted": false,
"rdnsMismatch": false
}
ChampTypeObligatoire
emailContentstring✅ (ou headers)
subjectstringNon (extrait de headers si absent)
fromstringNon
headersstringNon
spfstringNon (pass/fail/neutral)
dkimstringNon
dmarcstringNon
ipBlacklistedbooleanNon (défaut: false)
rdnsMismatchbooleanNon (défaut: false)

Réponse

{
"score": 78,
"patternScore": 85,
"headerScore": 45,
"threatLevel": "dangerous",
"severity": "high",
"is_spam": true,
"matchedPatterns": [
{
"patternId": "PHISH-001",
"type": "phishing",
"score": 72,
"severity": "high",
"reasons": [
"Mots-clés: colis, livraison",
"Domaine suspect: thepiratebuy.com"
],
"matchScore": 85
}
],
"headerFindings": [
{ "ruleId": "HDR-001", "check": "spf", "modifier": 25 },
{ "ruleId": "HDR-003", "check": "dmarc", "modifier": 20 }
],
"totalPatternsChecked": 13,
"totalRulesChecked": 5,
"summary": {
"patternsMatched": 1,
"topReasons": ["Mots-clés: colis, livraison", "Domaine suspect"],
"categories": ["phishing"]
},
"analyzedAt": "2026-04-19T10:00:00.000Z"
}

3. arcep-check

Méthode : POST
Réponse : JSON synchrone
Actions : check | info | refresh

Requête — check

{ "action": "check", "phoneNumber": "0612345678" }

Formats acceptés : 0612345678, +33612345678, 0033612345678, 06 12 34 56 78, 06-12-34-56-78

Réponse — check

{
"phoneNumber": "0612345678",
"normalized": "33612345678",
"isBlocked": true,
"matchCount": 2,
"matches": [
{ "name": "Démarchage commercial", "action": "block", "pattern": "336123#####" }
],
"database": {
"version": "v2026.02.26.17.26",
"total_blocked": 14000000
}
}

Requête — info

{ "action": "info" }

Réponse : version, description, nombre de patterns, date cache.

Requête — refresh

{ "action": "refresh" }

Force le rechargement de la base depuis Codeberg (ignore le cache 1h).


4. trigger-n8n-export

Méthode : POST
Réponse : JSON synchrone

Requête

{
"analysisData": {
"spf": "fail",
"dkim": "pass",
"dmarc": "fail",
"threatLevel": "high",
"from": "attacker@evil.com"
},
"n8nWebhookUrl": "https://your-n8n.com/webhook/abc123",
"exports": { "sheets": true, "xml": true, "json": true, "calendar": true }
}

Payload envoyé au webhook n8n

{
"timestamp": "2026-04-19T10:00:00.000Z",
"analysis": { "spf": "fail", ... },
"exports": { "sheets": true, ... },
"formats": {
"json": "{\"spf\":\"fail\",...}",
"xml": "<?xml version=\"1.0\"...>",
"csv": "spf,dkim,...\nfail,pass,...",
"calendar": "BEGIN:VCALENDAR\n..."
}
}

5. trigger-make-scenario

Méthode : POST
Réponse : JSON synchrone

Requête

{
"action": "analyze_spam",
"emailHeaders": "Received: from...",
"analysisData": { "spf": "fail", "threatLevel": "high" },
"customWebhookUrl": "https://hook.eu2.make.com/abc123"
}

Si customWebhookUrl et le secret MAKE_WEBHOOK_URL sont absents → erreur 400.


Système de crédits

  • 5 analyses/jour (reset automatique à minuit UTC)
  • Applicable aux endpoints analyze-email-headers et arcep-check
  • Géré côté client via localStorage (MVP) → migration vers DB en Q2 2026

Codes d'erreur communs

Code HTTPSignificationExemple
400Données manquantes ou invalidesemailHeaders absent
401Non authentifié (si verify_jwt activé)Token expiré
429Quota de crédits dépassé5 analyses/jour atteint
500Erreur serveur ou service tiers indisponibleARCEP unreachable

API REST v1 publique (Q2 2026)

L'API REST v1 sera disponible en Q2 2026 avec :

  • Authentification Bearer token dédié (distinct de la clé Supabase)
  • Rate limiting : 100 req/min, 10 000/jour
  • Documentation OpenAPI 3.0 + Swagger UI
  • Plans : Free / Pro / Enterprise avec quotas différenciés

Endpoints prévus :

POST /api/v1/email/analyze
POST /api/v1/phone/check
GET /api/v1/account/usage
GET /api/v1/account/credits

Voir ROADMAP.md pour le calendrier complet.