Documentazione API

Integra LegallyMail nella tua applicazione con la nostra API REST

Introduzione

L'API di LegallyMail ti permette di inviare email certificate dalla tua applicazione. Tutti gli invii vengono registrati e puoi ottenere certificati legali di consegna e lettura.

Nota: Solo gli utenti che hanno effettuato almeno un pagamento possono usare l'API e creare token.

Autenticazione

Tutte le richieste all'API richiedono autenticazione tramite un token di accesso. Puoi ottenere il tuo token dal pannello utente.

Metodo 1: Header Authorization

Includi il token nell'header Authorization:

Authorization: Bearer YOUR_TOKEN_HERE

Metodo 2: Nel corpo della richiesta

Includi il token nel JSON del corpo:

{
  "token": "YOUR_TOKEN_HERE",
  "recipient": "[email protected]",
  ...
}

Ottenere un Token

Per ottenere un token API, devi prima aver effettuato almeno un pagamento. Successivamente puoi creare token dal tuo pannello utente.

Accedi

Endpoint

POST /api/send

Invia un'email certificata.

Parametri
Parametro Tipo Richiesto Descrizione
Authorization string (header) Sì Token di autenticazione in formato "Bearer {token}" o solo il token
recipient / to string Sì Indirizzo email del destinatario
Destinatari Multipli: È possibile inviare a più destinatari separando gli indirizzi email con virgole (es: "[email protected], [email protected], [email protected]")
Limite predefinito: 10 destinatari. Questo limite può essere esteso su richiesta contattando il supporto.
subject string Sì Oggetto dell'email
message / body / content string (HTML) Sì Contenuto dell'email in formato HTML
reply_to string (email) No Indirizzo e-mail per le risposte. Deve essere un mittente registrato e verificato nel tuo account (opzionale).
reply_to_name string No Nome per le risposte (opzionale)
attachments array No Array di file allegati (opzionale). Ogni file deve avere: filename, content (base64), mime_type
webhook_url string No URL HTTPS pubblico in cui LegallyMail invierà un POST in tempo reale con gli eventi (opzionale).
webhook_secret string No Segreto condiviso per convalidare la firma HMAC-SHA256 nell'intestazione X-LegallyMail-Signature (opzionale ma consigliato).
Ejemplos de código
curl -X POST https://legallymail.com/api/send \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{
    "recipient": "[email protected], [email protected], [email protected]",
    "subject": "Test Email with Multiple Recipients",
    "message": "<h1>Hello World</h1><p>This email is sent to multiple recipients.</p>",
    "attachments": [
      {
        "filename": "document.pdf",
        "content": "base64_encoded_file_content_here",
        "mime_type": "application/pdf"
      }
    ]
  }'
<?php
$token = 'YOUR_TOKEN_HERE';
$url = 'https://legallymail.com/api/send';

// Leer el archivo y codificarlo en base64
$filePath = '/path/to/document.pdf';
$fileContent = base64_encode(file_get_contents($filePath));

$data = [
    'recipient' => '[email protected], [email protected], [email protected]',
    'subject' => 'Test Email with Multiple Recipients',
    'message' => '<h1>Hello World</h1><p>This email is sent to multiple recipients.</p>',
    'attachments' => [
        [
            'filename' => 'document.pdf',
            'content' => $fileContent,
            'mime_type' => 'application/pdf'
        ]
    ]
];

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer ' . $token,
    'Content-Type: application/json'
]);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response, true);
?>
import requests
import base64

token = 'YOUR_TOKEN_HERE'
url = 'https://legallymail.com/api/send'

# Leer el archivo y codificarlo en base64
with open('/path/to/document.pdf', 'rb') as file:
    file_content = base64.b64encode(file.read()).decode('utf-8')

data = {
    'recipient': '[email protected], [email protected], [email protected]',
    'subject': 'Test Email with Multiple Recipients',
    'message': '<h1>Hello World</h1><p>This email is sent to multiple recipients.</p>',
    'attachments': [
        {
            'filename': 'document.pdf',
            'content': file_content,
            'mime_type': 'application/pdf'
        }
    ]
}

headers = {
    'Authorization': f'Bearer {token}',
    'Content-Type': 'application/json'
}

response = requests.post(url, json=data, headers=headers)
result = response.json()
const token = 'YOUR_TOKEN_HERE';
const url = 'https://legallymail.com/api/send';

// Leer el archivo y codificarlo en base64 (Node.js)
const fs = require('fs');
const filePath = '/path/to/document.pdf';
const fileContent = fs.readFileSync(filePath).toString('base64');

const data = {
    recipient: '[email protected], [email protected], [email protected]',
    subject: 'Test Email with Multiple Recipients',
    message: '<h1>Hello World</h1><p>This email is sent to multiple recipients.</p>',
    attachments: [
        {
            filename: 'document.pdf',
            content: fileContent,
            mime_type: 'application/pdf'
        }
    ]
};

fetch(url, {
    method: 'POST',
    headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
    },
    body: JSON.stringify(data)
})
.then(response => response.json())
.then(result => console.log(result))
.catch(error => console.error('Error:', error));
package main

import (
    'bytes'
    'encoding/base64'
    'encoding/json'
    'io/ioutil'
    'net/http'
)

func main() {
    token := 'YOUR_TOKEN_HERE'
    url := 'https://legallymail.com/api/send'

    // Leer el archivo y codificarlo en base64
    filePath := '/path/to/document.pdf'
    fileContent, _ := ioutil.ReadFile(filePath)
    fileBase64 := base64.StdEncoding.EncodeToString(fileContent)

    data := map[string]interface{}{
        'recipient': '[email protected], [email protected], [email protected]',
        'subject':   'Test Email with Multiple Recipients',
        'message':   '<h1>Hello World</h1><p>This email is sent to multiple recipients.</p>',
        'attachments': []map[string]string{
            {
                'filename':  'document.pdf',
                'content':   fileBase64,
                'mime_type': 'application/pdf',
            },
        },
    }

    jsonData, _ := json.Marshal(data)

    req, _ := http.NewRequest('POST', url, bytes.NewBuffer(jsonData))
    req.Header.Set('Authorization', 'Bearer '+token)
    req.Header.Set('Content-Type', 'application/json')

    client := &http.Client{}
    resp, _ := client.Do(req)
    defer resp.Body.Close()
}
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;
import com.google.gson.Gson;

public class SendEmail {
    public static void main(String[] args) throws IOException, InterruptedException {
        String token = 'YOUR_TOKEN_HERE';
        String url = 'https://legallymail.com/api/send';

        // Leer el archivo y codificarlo en base64
        String filePath = '/path/to/document.pdf';
        byte[] fileBytes = Files.readAllBytes(Paths.get(filePath));
        String fileBase64 = Base64.getEncoder().encodeToString(fileBytes);

        Map<String, Object> attachment = new HashMap<>();
        attachment.put('filename', 'document.pdf');
        attachment.put('content', fileBase64);
        attachment.put('mime_type', 'application/pdf');

        Map<String, Object> data = new HashMap<>();
        data.put('recipient', '[email protected]');
        data.put('subject', 'Test Email with Attachment');
        data.put('message', '<h1>Hello World</h1><p>This email contains an attachment.</p>');
        data.put('attachments', Arrays.asList(attachment));

        Gson gson = new Gson();
        String jsonData = gson.toJson(data);

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(url))
            .header('Authorization', 'Bearer ' + token)
            .header('Content-Type', 'application/json')
            .POST(HttpRequest.BodyPublishers.ofString(jsonData))
            .build();

        HttpClient client = HttpClient.newHttpClient();
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
    }
}
const https = require('https');
const fs = require('fs');

const token = 'YOUR_TOKEN_HERE';
const url = 'https://legallymail.com/api/send';

// Leer el archivo y codificarlo en base64
const filePath = '/path/to/document.pdf';
const fileContent = fs.readFileSync(filePath).toString('base64');

const data = {
    recipient: '[email protected]',
    subject: 'Test Email with Attachment',
    message: '<h1>Hello World</h1><p>This email contains an attachment.</p>',
    attachments: [
        {
            filename: 'document.pdf',
            content: fileContent,
            mime_type: 'application/pdf'
        }
    ]
};

const options = {
    hostname: 'legallymail.com',
    path: '/api/send',
    method: 'POST',
    headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
    }
};

const req = https.request(options, (res) => {
    let responseData = '';
    res.on('data', (chunk) => {
        responseData += chunk;
    });
    res.on('end', () => {
        const result = JSON.parse(responseData);
        console.log(result);
    });
});

req.on('error', (error) => {
    console.error('Error:', error);
});

req.write(JSON.stringify(data));
req.end();
using System;
using System.Net.Http;
using System.Text;
using System.IO;
using Newtonsoft.Json;

class Program
{
    static async System.Threading.Tasks.Task Main()
    {
        string token = 'YOUR_TOKEN_HERE';
        string url = 'https://legallymail.com/api/send';

        // Leer el archivo y codificarlo en base64
        string filePath = '/path/to/document.pdf';
        byte[] fileBytes = File.ReadAllBytes(filePath);
        string fileBase64 = Convert.ToBase64String(fileBytes);

        var attachment = new
        {
            filename = 'document.pdf',
            content = fileBase64,
            mime_type = 'application/pdf'
        };

        var data = new
        {
            recipient = '[email protected], [email protected], [email protected]',
            subject = 'Test Email with Multiple Recipients',
            message = '<h1>Hello World</h1><p>This email is sent to multiple recipients.</p>',
            attachments = new[] { attachment }
        };

        string jsonData = JsonConvert.SerializeObject(data);

        using (var client = new HttpClient())
        {
            client.DefaultRequestHeaders.Add('Authorization', 'Bearer ' + token);
            var content = new StringContent(jsonData, Encoding.UTF8, 'application/json');

            var response = await client.PostAsync(url, content);
            string result = await response.Content.ReadAsStringAsync();
            Console.WriteLine(result);
        }
    }
}
Risposta riuscita
{
  "success": true,
  "message": "Email sent to 2 of 2 recipients",
  "data": {
    "tracking_id": "cm_parent_abc123",
    "recipients": [
      {
        "email": "[email protected]",
        "status": "sent",
        "tracking_id": "cm_rec1_xyz",
        "error_details": null
      },
      {
        "email": "[email protected]",
        "status": "sent",
        "tracking_id": "cm_rec2_abc",
        "error_details": null
      }
    ],
    "subject": "Test Email",
    "sent_at": "2024-01-15 10:30:00",
    "success_count": 2,
    "fail_count": 0,
    "error_status": null
  }
}
GET /api/info

Ottiene informazioni sull'API e l'utente autenticato

Restituisce informazioni sull'API e l'utente autenticato, inclusi email, piano sottoscritto ed email rimanenti.

Parametri
Parametro Tipo Richiesto Descrizione
Authorization string (header) Sì Token di autenticazione in formato "Bearer {token}" o solo il token
Ejemplos de código
curl -X GET https://legallymail.com/api/info \
  -H "Authorization: Bearer YOUR_TOKEN_HERE"
<?php
$token = 'YOUR_TOKEN_HERE';
$url = 'https://legallymail.com/api/info';

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer ' . $token
]);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response, true);
?>
import requests

token = 'YOUR_TOKEN_HERE'
url = 'https://legallymail.com/api/info'

headers = {
    'Authorization': f'Bearer {token}'
}

response = requests.get(url, headers=headers)
result = response.json()
const token = 'YOUR_TOKEN_HERE';
const url = 'https://legallymail.com/api/info';

fetch(url, {
    method: 'GET',
    headers: {
        'Authorization': `Bearer ${token}`
    }
})
.then(response => response.json())
.then(result => console.log(result))
.catch(error => console.error('Error:', error));
package main

import (
    'net/http'
    'io/ioutil'
)

func main() {
    token := 'YOUR_TOKEN_HERE'
    url := 'https://legallymail.com/api/info'

    req, _ := http.NewRequest('GET', url, nil)
    req.Header.Set('Authorization', 'Bearer '+token)

    client := &http.Client{}
    resp, _ := client.Do(req)
    defer resp.Body.Close()

    body, _ := ioutil.ReadAll(resp.Body)
}
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;

public class GetInfo {
    public static void main(String[] args) throws Exception {
        String token = 'YOUR_TOKEN_HERE';
        String url = 'https://legallymail.com/api/info';

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(url))
            .header('Authorization', 'Bearer ' + token)
            .GET()
            .build();

        HttpClient client = HttpClient.newHttpClient();
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        String result = response.body();
    }
}
const https = require('https');

const token = 'YOUR_TOKEN_HERE';
const url = 'https://legallymail.com/api/info';

const options = {
    hostname: 'legallymail.com',
    path: '/api/info',
    method: 'GET',
    headers: {
        'Authorization': `Bearer ${token}`
    }
};

const req = https.request(options, (res) => {
    let responseData = '';
    res.on('data', (chunk) => {
        responseData += chunk;
    });
    res.on('end', () => {
        const result = JSON.parse(responseData);
        console.log(result);
    });
});

req.on('error', (error) => {
    console.error('Error:', error);
});

req.end();
using System;
using System.Net.Http;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        string token = 'YOUR_TOKEN_HERE';
        string url = 'https://legallymail.com/api/info';

        using (var client = new HttpClient())
        {
            client.DefaultRequestHeaders.Add('Authorization', 'Bearer ' + token);
            
            var response = await client.GetAsync(url);
            string result = await response.Content.ReadAsStringAsync();
            Console.WriteLine(result);
        }
    }
}
Risposta riuscita
{
  "name": "LegallyMail API",
  "version": "1.0.0",
  "user": {
    "email": "[email protected]",
    "name": "Nombre Usuario",
    "plan": "professional",
    "plan_name": "Profesional",
    "plan_limit": 100,
    "extra_emails": 0,
    "remaining_emails": 75
  }
}
GET /api/emails

Ottiene le ultime email inviate

Restituisce un elenco delle ultime email inviate dall'utente autenticato, incluso il loro stato (sent, read, failed). Massimo 25 risultati.

Parametri
Parametro Tipo Richiesto Descrizione
Authorization string (header) Sì Token di autenticazione in formato "Bearer {token}" o solo il token
limit integer (query) Sì Numero di email da restituire (tra 1 e 25). Obbligatorio.
Ejemplos de código
curl -X GET "https://legallymail.com/api/emails?limit=10" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE"
<?php
$token = 'YOUR_TOKEN_HERE';
$url = 'https://legallymail.com/api/emails?limit=10';

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer ' . $token
]);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response, true);
?>
import requests

token = 'YOUR_TOKEN_HERE'
url = 'https://legallymail.com/api/emails'
params = {'limit': 10}

headers = {
    'Authorization': f'Bearer {token}'
}

response = requests.get(url, params=params, headers=headers)
result = response.json()
const token = 'YOUR_TOKEN_HERE';
const url = 'https://legallymail.com/api/emails?limit=10';

fetch(url, {
    method: 'GET',
    headers: {
        'Authorization': `Bearer ${token}`
    }
})
.then(response => response.json())
.then(result => console.log(result))
.catch(error => console.error('Error:', error));
package main

import (
    'net/http'
    'io/ioutil'
)

func main() {
    token := 'YOUR_TOKEN_HERE'
    url := 'https://legallymail.com/api/emails?limit=10'

    req, _ := http.NewRequest('GET', url, nil)
    req.Header.Set('Authorization', 'Bearer '+token)

    client := &http.Client{}
    resp, _ := client.Do(req)
    defer resp.Body.Close()

    body, _ := ioutil.ReadAll(resp.Body)
}
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;

public class GetEmails {
    public static void main(String[] args) throws Exception {
        String token = 'YOUR_TOKEN_HERE';
        String url = 'https://legallymail.com/api/emails?limit=10';

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(url))
            .header('Authorization', 'Bearer ' + token)
            .GET()
            .build();

        HttpClient client = HttpClient.newHttpClient();
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        String result = response.body();
    }
}
const https = require('https');

const token = 'YOUR_TOKEN_HERE';
const url = 'https://legallymail.com/api/emails?limit=10';

const options = {
    hostname: 'legallymail.com',
    path: '/api/emails?limit=10',
    method: 'GET',
    headers: {
        'Authorization': `Bearer ${token}`
    }
};

const req = https.request(options, (res) => {
    let responseData = '';
    res.on('data', (chunk) => {
        responseData += chunk;
    });
    res.on('end', () => {
        const result = JSON.parse(responseData);
        console.log(result);
    });
});

req.on('error', (error) => {
    console.error('Error:', error);
});

req.end();
using System;
using System.Net.Http;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        string token = 'YOUR_TOKEN_HERE';
        string url = 'https://legallymail.com/api/emails?limit=10';

        using (var client = new HttpClient())
        {
            client.DefaultRequestHeaders.Add('Authorization', 'Bearer ' + token);
            
            var response = await client.GetAsync(url);
            string result = await response.Content.ReadAsStringAsync();
            Console.WriteLine(result);
        }
    }
}
Risposta riuscita
{
  "success": true,
  "count": 2,
  "limit": 10,
  "max_limit": 25,
  "emails": [
    {
      "id": 123,
      "tracking_id": "cm_abc123",
      "recipient": "[email protected]",
      "subject": "Test Email",
      "status": "read",
      "sent_at": "2024-01-15 10:30:00",
      "read_at": "2024-01-15 11:00:00",
      "created_at": "2024-01-15 10:30:00",
      "mailsender_id": "mail_xyz789",
      "is_multi_recipient": false,
      "recipient_count": 1,
      "error_status": null
    },
    {
      "id": 122,
      "tracking_id": "cm_def456",
      "recipient": "Multiple Recipients",
      "subject": "Group Email",
      "status": "pending",
      "sent_at": "2024-01-14 15:20:00",
      "read_at": null,
      "created_at": "2024-01-14 15:20:00",
      "mailsender_id": "mail_uvw012",
      "is_multi_recipient": true,
      "recipient_count": 5,
      "error_status": "Soft Bounce"
    }
  ]
}
GET /api/email

Ottiene lo stato di un'email specifica

Restituisce le informazioni dettagliate di un'email specifica identificata dal suo tracking_id, incluso il suo stato attuale (sent, read, failed), le date di invio e lettura, e i certificati di timestamp (TSQ/TSR) se esistono.

Parametri
Parametro Tipo Richiesto Descrizione
Authorization string (header) Sì Token di autenticazione in formato "Bearer {token}" o solo il token
tracking_id string (query) Sì L'identificatore unico di tracciamento dell'email (tracking_id) restituito all'invio dell'email.
Ejemplos de código
curl -X GET "https://legallymail.com/api/email?tracking_id=cm_abc123" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE"
<?php
$token = 'YOUR_TOKEN_HERE';
$trackingId = 'cm_abc123';
$url = 'https://legallymail.com/api/email?tracking_id=' . urlencode($trackingId);

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    'Authorization: Bearer ' . $token
]);

$response = curl_exec($ch);
curl_close($ch);

$result = json_decode($response, true);
?>
import requests

token = 'YOUR_TOKEN_HERE'
url = 'https://legallymail.com/api/email'
params = {'tracking_id': 'cm_abc123'}

headers = {
    'Authorization': f'Bearer {token}'
}

response = requests.get(url, params=params, headers=headers)
result = response.json()
const token = 'YOUR_TOKEN_HERE';
const trackingId = 'cm_abc123';
const url = `https://legallymail.com/api/email?tracking_id=${trackingId}`;

fetch(url, {
    method: 'GET',
    headers: {
        'Authorization': `Bearer ${token}`
    }
})
.then(response => response.json())
.then(result => console.log(result))
.catch(error => console.error('Error:', error));
package main

import (
    'net/http'
    'io/ioutil'
    'net/url'
)

func main() {
    token := 'YOUR_TOKEN_HERE'
    trackingId := 'cm_abc123'
    baseUrl := 'https://legallymail.com/api/email'
    
    params := url.Values{}
    params.Add('tracking_id', trackingId)
    fullUrl := baseUrl + '?' + params.Encode()

    req, _ := http.NewRequest('GET', fullUrl, nil)
    req.Header.Set('Authorization', 'Bearer '+token)

    client := &http.Client{}
    resp, _ := client.Do(req)
    defer resp.Body.Close()

    body, _ := ioutil.ReadAll(resp.Body)
}
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;

public class GetEmail {
    public static void main(String[] args) throws Exception {
        String token = 'YOUR_TOKEN_HERE';
        String trackingId = 'cm_abc123';
        String url = 'https://legallymail.com/api/email?tracking_id=' + trackingId;

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(url))
            .header('Authorization', 'Bearer ' + token)
            .GET()
            .build();

        HttpClient client = HttpClient.newHttpClient();
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        String result = response.body();
    }
}
const https = require('https');

const token = 'YOUR_TOKEN_HERE';
const trackingId = 'cm_abc123';
const url = `https://legallymail.com/api/email?tracking_id=${trackingId}`;

const options = {
    hostname: 'legallymail.com',
    path: `/api/email?tracking_id=${trackingId}`,
    method: 'GET',
    headers: {
        'Authorization': `Bearer ${token}`
    }
};

const req = https.request(options, (res) => {
    let responseData = '';
    res.on('data', (chunk) => {
        responseData += chunk;
    });
    res.on('end', () => {
        const result = JSON.parse(responseData);
        console.log(result);
    });
});

req.on('error', (error) => {
    console.error('Error:', error);
});

req.end();
using System;
using System.Net.Http;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        string token = 'YOUR_TOKEN_HERE';
        string trackingId = 'cm_abc123';
        string url = $"https://legallymail.com/api/email?tracking_id={trackingId}";

        using (var client = new HttpClient())
        {
            client.DefaultRequestHeaders.Add('Authorization', 'Bearer ' + token);
            
            var response = await client.GetAsync(url);
            string result = await response.Content.ReadAsStringAsync();
            Console.WriteLine(result);
        }
    }
}
Risposta riuscita
{
  "success": true,
  "email": {
    "id": 123,
    "tracking_id": "cm_abc123",
    "recipient": "Multiple Recipients",
    "subject": "Group Email",
    "status": "pending",
    "sent_at": "2024-01-15 10:30:00",
    "read_at": null,
    "created_at": "2024-01-15 10:30:00",
    "mailsender_id": "mail_xyz789",
    "is_multi_recipient": true,
    "recipient_count": 2,
    "error_status": null,
    "certificates": [],
    "recipients": [
      {
        "email": "[email protected]",
        "name": "Recipient One",
        "status": "read",
        "tracking_id": "cm_rec1_xyz",
        "sent_at": "2024-01-15 10:30:05",
        "delivered_at": "2024-01-15 10:30:10",
        "read_at": "2024-01-15 11:00:00",
        "error_details": null,
        "certificates": [
          {
            "certificate_type": "read",
            "file_hash": "sha256_hash_read_1",
            "confirmed_at": "2024-01-15 11:00:00",
            "has_tsr": true,
            "has_tsq": true,
            "pdf_download_url": "https://legallymail.com/emails/download-certificate?id=cm_rec1_xyz&status=read"
          }
        ]
      },
      {
        "email": "[email protected]",
        "name": "Recipient Two",
        "status": "delivered",
        "tracking_id": "cm_rec2_abc",
        "sent_at": "2024-01-15 10:30:05",
        "delivered_at": "2024-01-15 10:30:12",
        "read_at": null,
        "error_details": "Recipient inbox full",
        "certificates": [
          {
            "certificate_type": "delivered",
            "file_hash": "sha256_hash_del_2",
            "confirmed_at": "2024-01-15 10:30:12",
            "has_tsr": true,
            "has_tsq": true,
            "pdf_download_url": "https://legallymail.com/emails/download-certificate?id=cm_rec2_abc&status=delivered"
          }
        ]
      }
    ]
  }
}
POST /api/signature/send

Richiedi una firma elettronica

Crea una nuova richiesta di firma elettronica inviando un documento in formato base64.

Parametri
Parametro Tipo Richiesto Descrizione
signer_name string Sì Nome completo del firmatario.
signer_email string Sì Email del firmatario dove riceverà la richiesta.
signer_phone string No Telefono del firmatario per la validazione SMS (opzionale).
signers array No Array di firmatari del documento (max 5). Ogni firmatario deve avere: name, email, phone (opzionale).
document string (base64) Sì Contenuto del documento PDF codificato in base64 (max 10MB).
document_name string Sì Nome del file che il firmatario vedrà.
language string No Lingua per l'interfaccia di firma e le email (es, en, fr, it). (default: es)
webhook_url string No URL HTTPS pubblico in cui LegallyMail invierà un POST in tempo reale con gli eventi (opzionale).
webhook_secret string No Segreto condiviso per convalidare la firma HMAC-SHA256 nell'intestazione X-LegallyMail-Signature (opzionale ma consigliato).
Ejemplos de código
curl -X POST https://legallymail.com/api/signature/send \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{
    "signer_name": "John Doe",
    "signer_email": "[email protected]",
    "document": "BASE64_CONTENT_HERE",
    "document_name": "Contract.pdf",
    "language": "es"
  }'
Esempio multi-firmatario (fino a 5)
curl -X POST https://legallymail.com/api/signature/send \
  -H "Authorization: Bearer YOUR_TOKEN_HERE" \
  -H "Content-Type: application/json" \
  -d '{
    "signers": [
        {"name": "Firmante 1", "email": "[email protected]"},
        {"name": "Firmante 2", "email": "[email protected]", "phone": "+34600000000"}
    ],
    "document": "BASE64_CONTENT_HERE",
    "document_name": "Contract.pdf",
    "language": "es"
  }'
<?php
$url = "https://legallymail.com/api/signature/send";
$token = "YOUR_TOKEN_HERE";
$data = [
    "signer_name" => "John Doe",
    "signer_email" => "[email protected]",
    "document" => base64_encode(file_get_contents("document.pdf")),
    "document_name" => "Contract.pdf",
    "language" => "es"
];

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "Authorization: Bearer $token",
    "Content-Type: application/json"
]);

$response = curl_exec($ch);
curl_close($ch);
echo $response;
?>
Esempio multi-firmatario (PHP)
<?php
$url = "https://legallymail.com/api/signature/send";
$token = "YOUR_TOKEN_HERE";
$data = [
    "signers" => [
        ["name" => "Firmante 1", "email" => "[email protected]"],
        ["name" => "Firmante 2", "email" => "[email protected]", "phone" => "+34600000000"]
    ],
    "document" => base64_encode(file_get_contents("document.pdf")),
    "document_name" => "Contract.pdf",
    "language" => "es"
];

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "Authorization: Bearer $token",
    "Content-Type: application/json"
]);

$response = curl_exec($ch);
curl_close($ch);
echo $response;
?>
import requests
import base64

url = "https://legallymail.com/api/signature/send"
token = "YOUR_TOKEN_HERE"

with open("document.pdf", "rb") as f:
    doc_base64 = base64.b64encode(f.read()).decode("utf-8")

payload = {
    "signer_name": "John Doe",
    "signer_email": "[email protected]",
    "document": doc_base64,
    "document_name": "Contract.pdf",
    "language": "es"
}

headers = {
    "Authorization": f"Bearer {token}",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)
print(response.json())
2622:
Esempio multi-firmatario (Python)
import requests
 import base64
 
 url = "https://legallymail.com/api/signature/send"
 token = "YOUR_TOKEN_HERE"
 
 with open("document.pdf", "rb") as f:
     doc_base64 = base64.b64encode(f.read()).decode("utf-8")
 
 payload = {
     "signers": [
         {"name": "Firmante 1", "email": "[email protected]"},
         {"name": "Firmante 2", "email": "[email protected]", "phone": "+34600000000"}
     ],
     "document": doc_base64,
     "document_name": "Contract.pdf",
     "language": "es"
 }
 
 headers = {
     "Authorization": f"Bearer {token}",
     "Content-Type": "application/json"
 }
 
 response = requests.post(url, json=payload, headers=headers)
 print(response.json())
const url = "https://legallymail.com/api/signature/send";
const token = "YOUR_TOKEN_HERE";

const payload = {
    signer_name: "John Doe",
    signer_email: "[email protected]",
    document: "BASE64_CONTENT_HERE",
    document_name: "Contract.pdf",
    language: "es"
};

fetch(url, {
    method: "POST",
    headers: {
        "Authorization": `Bearer ${token}`,
        "Content-Type": "application/json"
    },
    body: JSON.stringify(payload)
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error("Error:", error));
2672:
Esempio multi-firmatario (JavaScript)
const url = "https://legallymail.com/api/signature/send";
 const token = "YOUR_TOKEN_HERE";
 
 const payload = {
     signers: [
         {name: "Firmante 1", email: "[email protected]"},
         {name: "Firmante 2", email: "[email protected]", phone: "+34600000000"}
     ],
     document: "BASE64_CONTENT_HERE",
     document_name: "Contract.pdf",
     language: "es"
 };
 
 fetch(url, {
     method: "POST",
     headers: {
         "Authorization": `Bearer ${token}`,
         "Content-Type": "application/json"
     },
     body: JSON.stringify(payload)
 })
 .then(response => response.json())
 .then(data => console.log(data))
 .catch(error => console.error("Error:", error));
package main

import (
    "bytes"
    "encoding/json"
    "net/http"
)

func main() {
    url := "https://legallymail.com/api/signature/send"
    token := "YOUR_TOKEN_HERE"

    payload := map[string]string{
        "signer_name":  "John Doe",
        "signer_email": "[email protected]",
        "document":      "BASE64_CONTENT_HERE",
        "document_name": "Contract.pdf",
        "language":      "es",
    }
    jsonPayload, _ := json.Marshal(payload)

    req, _ := http.NewRequest("POST", url, bytes.NewBuffer(jsonPayload))
    req.Header.Set("Authorization", "Bearer "+token)
    req.Header.Set("Content-Type", "application/json")

    client := &http.Client{}
    resp, _ := client.Do(req)
    defer resp.Body.Close()
}
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;

public class SendSignature {
    public static void main(String[] args) throws Exception {
        String url = "https://legallymail.com/api/signature/send";
        String token = "YOUR_TOKEN_HERE";
        String json = "{\"signer_name\":\"John Doe\",\"signer_email\":\"[email protected]\",\"document\":\"BASE64\",\"document_name\":\"Contract.pdf\"}";

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create(url))
            .header("Authorization", "Bearer " + token)
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(json))
            .build();

        HttpClient client = HttpClient.newHttpClient();
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.body());
    }
}
const axios = require('axios');
const fs = require('fs');

const url = "https://legallymail.com/api/signature/send";
const token = "YOUR_TOKEN_HERE";

const docBase64 = fs.readFileSync('document.pdf', { encoding: 'base64' });

const payload = {
    signer_name: "John Doe",
    signer_email: "[email protected]",
    document: docBase64,
    document_name: "Contract.pdf",
    language: "es"
};

axios.post(url, payload, {
    headers: {
        'Authorization': `Bearer ${token}`,
        'Content-Type': 'application/json'
    }
})
.then(response => console.log(response.data))
.catch(error => console.error(error));
using System;
using System.Net.Http;
using System.Text;
using System.IO;
using Newtonsoft.Json;

class Program {
    static async System.Threading.Tasks.Task Main() {
        string url = "https://legallymail.com/api/signature/send";
        string token = "YOUR_TOKEN_HERE";
        
        byte[] fileBytes = File.ReadAllBytes("document.pdf");
        string docBase64 = Convert.ToBase64String(fileBytes);

        var payload = new {
            signer_name = "John Doe",
            signer_email = "[email protected]",
            document = docBase64,
            document_name = "Contract.pdf",
            language = "es"
        };

        using (var client = new HttpClient()) {
            client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
            var content = new StringContent(JsonConvert.SerializeObject(payload), Encoding.UTF8, "application/json");
            var response = await client.PostAsync(url, content);
            Console.WriteLine(await response.Content.ReadAsStringAsync());
        }
    }
}
Risposta riuscita
{
  "success": true,
  "message": "Signature request sent successfully",
  "data": {
    "uuid": "550e8400-e29b-41d4-a716-446655440000",
    "signer_name": "John Doe",
    "signer_email": "[email protected]",
    "document_name": "Contract.pdf",
    "status": "pending",
    "created_at": "2024-01-15 10:30:00"
  }
}
GET /api/signatures

Elenca le tue richieste di firma

Restituisce un elenco delle richieste di firma elettronica effettuate.

Parametri
Parametro Tipo Richiesto Descrizione
limit int No Numero di email da restituire (tra 1 e 25). Obbligatorio. (max 25)
Ejemplos de código
curl -X GET "https://legallymail.com/api/signatures?limit=10" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE"
<?php
$token = "YOUR_TOKEN_HERE";
$url = "https://legallymail.com/api/signatures?limit=10";

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "Authorization: Bearer $token"
]);

$response = curl_exec($ch);
curl_close($ch);
echo $response;
?>
import requests

url = "https://legallymail.com/api/signatures"
headers = {"Authorization": "Bearer YOUR_TOKEN_HERE"}
params = {"limit": 10}

response = requests.get(url, headers=headers, params=params)
print(response.json())
fetch("https://legallymail.com/api/signatures?limit=10", {
    headers: {
        "Authorization": "Bearer YOUR_TOKEN_HERE"
    }
})
.then(res => res.json())
.then(data => console.log(data));
package main
import ("net/http"; "io/ioutil")

func main() {
    req, _ := http.NewRequest("GET", "https://legallymail.com/api/signatures?limit=10", nil)
    req.Header.Set("Authorization", "Bearer YOUR_TOKEN_HERE")
    client := &http.Client{}
    resp, _ := client.Do(req)
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    _ = body
}
import java.net.http.*;
import java.net.URI;

public class Main {
    public static void main(String[] args) throws Exception {
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest req = HttpRequest.newBuilder()
            .uri(URI.create("https://legallymail.com/api/signatures?limit=10"))
            .header("Authorization", "Bearer YOUR_TOKEN_HERE")
            .build();
        System.out.println(client.send(req, HttpResponse.BodyHandlers.ofString()).body());
    }
}
const axios = require("axios");
axios.get("https://legallymail.com/api/signatures?limit=10", {
    headers: { Authorization: "Bearer YOUR_TOKEN_HERE" }
}).then(res => console.log(res.data));
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer YOUR_TOKEN_HERE");
var res = await client.GetStringAsync("https://legallymail.com/api/signatures?limit=10");
Console.WriteLine(res);
Risposta riuscita
{
  "success": true,
  "data": [
    {
      "uuid": "550e8400-e29b-41d4-a716-446655440000",
      "signer_name": "John Doe",
      "signer_email": "[email protected]",
      "document_name": "Contract.pdf",
      "status": "signed",
      "created_at": "2024-01-15 10:30:00",
      "signed_at": "2024-01-15 11:00:00"
    },
    ...
  ]
}
GET /api/signature

Controlla lo stato di una firma

Ottieni i dettagli e lo stato attuale di una specifica richiesta di firma.

Parametri
Parametro Tipo Richiesto Descrizione
uuid string Sì L'identificatore unico (UUID) della firma elettronica.
Ejemplos de código
curl -X GET "https://legallymail.com/api/signature?uuid=YOUR_UUID_HERE" \
  -H "Authorization: Bearer YOUR_TOKEN_HERE"
<?php
$token = "YOUR_TOKEN_HERE";
$uuid = "YOUR_UUID_HERE";
$url = "https://legallymail.com/api/signature?uuid=$uuid";

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer $token"]);
$response = curl_exec($ch);
curl_close($ch);
echo $response;
?>
import requests
url = "https://legallymail.com/api/signature"
headers = {"Authorization": "Bearer YOUR_TOKEN_HERE"}
params = {"uuid": "YOUR_UUID_HERE"}
response = requests.get(url, headers=headers, params=params)
print(response.json())
fetch("https://legallymail.com/api/signature?uuid=YOUR_UUID_HERE", {
    headers: { Authorization: "Bearer YOUR_TOKEN_HERE" }
}).then(res => res.json()).then(data => console.log(data));
package main
import ("net/http"; "io/ioutil")
func main() {
    req, _ := http.NewRequest("GET", "https://legallymail.com/api/signature?uuid=YOUR_UUID_HERE", nil)
    req.Header.Set("Authorization", "Bearer YOUR_TOKEN_HERE")
    client := &http.Client{}
    resp, _ := client.Do(req)
    defer resp.Body.Close()
    body, _ := ioutil.ReadAll(resp.Body)
    _ = body
}
import java.net.http.*;
import java.net.URI;
public class Main {
    public static void main(String[] args) throws Exception {
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest req = HttpRequest.newBuilder()
            .uri(URI.create("https://legallymail.com/api/signature?uuid=YOUR_UUID_HERE"))
            .header("Authorization", "Bearer YOUR_TOKEN_HERE")
            .build();
        System.out.println(client.send(req, HttpResponse.BodyHandlers.ofString()).body());
    }
}
const axios = require("axios");
axios.get("https://legallymail.com/api/signature?uuid=YOUR_UUID_HERE", {
    headers: { Authorization: "Bearer YOUR_TOKEN_HERE" }
}).then(res => console.log(res.data));
using var client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer YOUR_TOKEN_HERE");
var res = await client.GetStringAsync("https://legallymail.com/api/signature?uuid=YOUR_UUID_HERE");
Console.WriteLine(res);
Risposta riuscita
{
  "success": true,
  "data": {
    "uuid": "550e8400-e29b-41d4-a716-446655440000",
    "signer_name": "John Doe",
    "signer_email": "[email protected]",
    "document_name": "Contract.pdf",
    "status": "signed",
    "created_at": "2024-01-15 10:30:00",
    "signed_at": "2024-01-15 11:00:00",
    "expires_at": "2024-01-22 10:30:00",
    "document_hash": "...",
    "signed_document_hash": "...",
    "signers": [
      {
        "signer_name": "John Doe",
        "signer_email": "[email protected]",
        "signer_phone": "+34600000000",
        "status": "signed",
        "signed_at": "2024-01-15 11:00:00"
      }
    ],
    "download_original_url": "https://legallymail.com/dashboard/electronic-signature/download-original/...",
    "download_acta_url": "https://legallymail.com/dashboard/electronic-signature/download-acta/..."
  }
}
POST /api/sms/send

Invia un SMS certificato. Viene detratto 1 credito SMS certificato dal tuo account (fino a 320 caratteri).

Parametri
Parametro Tipo Richiesto Descrizione
Authorization string (header) Sì api.documentation.auth_desc
phone / recipient string Sì Numero di telefono con prefisso internazionale (es: +34600000000).
message / text string Sì Contenuto dell'SMS. 1 credito per messaggio fino a 320 caratteri.
Risposta riuscita
{
  "success": true,
  "uuid": "...",
  "status": "sent",
  "credits_deducted": 1,
  "message": "Certified SMS sent successfully"
}
GET /api/sms

Ottiene lo stato di un SMS certificato precedentemente inviato.

Parametri
Parametro Tipo Richiesto Descrizione
Authorization string (header) Sì api.documentation.auth_desc
uuid / id string (query) Sì Identificatore unico dell'SMS restituito al momento dell'invio.
Risposta riuscita
{
  "success": true,
  "data": {
    "uuid": "...",
    "recipient": "+34600000000",
    "message": "Contenido del SMS",
    "status": "delivered",
    "credits": 1,
    "sent_at": "2024-01-15 10:30:00",
    "delivered_at": "2024-01-15 10:30:05",
    "failed_at": null,
    "ts_confirmed_at": "2024-01-15 10:30:10",
    "download_certificate_url": "https://legallymail.com/dashboard/certified-sms/download-certificate/..."
  }
}
WEBHOOKS HMAC-SHA256

Webhook in Tempo Reale

Ricevi eventi istantanei (consegna, lettura, firma) con firma crittografica senza bisogno di polling o cronjobs continui.

Architettura Orientata agli Eventi: Zero Polling

Interrogare lo stato tramite chiamate continue (GET /api/email, GET /api/signature) sovraccarica i server e introduce latenza inutile. Il nostro sistema di webhook invia un HTTP POST nell'esatto millisecondo in cui l'email viene consegnata, aperta o quando viene completata la firma eIDAS.

1. ¿Cómo configurar Webhooks?

Puedes configurar el endpoint receptor de dos formas:

Por Envío (Granular)

Incluye los parámetros opcionales webhook_url y webhook_secret directamente en el cuerpo JSON de tus peticiones POST /api/send o POST /api/signature/send.

Nivel de Cuenta

Configura y gestiona tus endpoints de webhook directamente desde tu panel de usuario para recibir automáticamente todos los eventos de correos y firmas.

2. Eventi Disponibili

Evento Servicio Disparador y Contenido
email.sent Correo Certificado Se emite inmediatamente cuando el correo certificado es procesado y aceptado en cola por los servidores de envío de LegallyMail.
email.delivered Correo Certificado Se despacha en el milisegundo en que el servidor MX del destinatario confirma la recepción del mensaje con evidencia SHA-256.
email.opened Correo Certificado Se dispara cuando el destinatario abre o visualiza el correo certificado (primera lectura fehaciente confirmada).
email.failed Correo Certificado Se notifica si la entrega falla de forma definitiva (rebote duro, buzón inexistente o bloqueo) con el código de diagnóstico SMTP.
signature.completed Firma Electrónica Se envía cuando todos los firmantes han completado su rúbrica eIDAS. Incluye URLs para descarga del PDF sellado y del acta probatoria.
signature.rejected Firma Electrónica Se despacha si un firmante rechaza formalmente la firma, adjuntando el motivo y fecha exacta.
sms.delivered SMS Certificado Confirmación inmediata de entrega del SMS certificado por parte de la red del operador móvil.

3. Sicurezza e Validazione della Firma HMAC-SHA256

Cada petición enviada por LegallyMail a tu servidor incluye encabezados HTTP de seguridad que garantizan que el webhook proviene legítimamente de nuestros servidores y protegen contra ataques de repetición (Replay Attacks):

Encabezado HTTP Descripción y Formato
X-LegallyMail-Signature Firma criptográfica HMAC-SHA256 con formato: t={timestamp},v1={hash_hex}
X-LegallyMail-Timestamp Marca temporal UNIX en segundos del momento de emisión del evento.
X-LegallyMail-Event Nombre del evento disparado (ej: email.delivered, signature.completed).
X-LegallyMail-Delivery Identificador único del despacho (UUID v4) para garantizar idempotencia en tu backend.
User-Agent LegallyMail-Webhooks/1.0 (+https://legallymail.com)

4. Estructura del Payload JSON

Simulador Interactivo de Webhooks en Vivo: Puedes probar la recepción y verificación de tu firma HMAC al instante desde el Simulador de Eventos en /api-tokens#webhooks con datos simulados y respuestas en tiempo real.

{
  "id": "evt_94a7e3d12c9b",
  "event": "email.delivered",
  "timestamp": 1729722300,
  "created_at": "2026-10-23T22:30:15+02:00",
  "api_version": "2026-01-01",
  "data": {
    "tracking_id": "em_8f431b920ca7",
    "recipient": "[email protected]",
    "subject": "Comunicación formal de fin de contrato",
    "status": "delivered",
    "sent_at": "2026-10-23 22:30:02",
    "delivered_at": "2026-10-23 22:30:15",
    "delivery_hash": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
    "delivery_ts_confirmed": "2026-10-23 22:30:15",
    "download_certificate_url": "https://legallymail.com/emails/certificate/em_8f431b920ca7"
  }
}

{
  "id": "evt_6b1e84fa920d",
  "event": "email.opened",
  "timestamp": 1729722340,
  "created_at": "2026-10-23T22:30:55+02:00",
  "api_version": "2026-01-01",
  "data": {
    "tracking_id": "em_8f431b920ca7",
    "recipient": "[email protected]",
    "subject": "Comunicación formal de fin de contrato",
    "status": "opened",
    "sent_at": "2026-10-23 22:30:02",
    "delivered_at": "2026-10-23 22:30:15",
    "read_at": "2026-10-23 22:30:55",
    "read_hash": "a45f93b18c0e9b119dc2345eaf8896fb92427ae41e4649b934ca495991b78912",
    "read_ts_confirmed": "2026-10-23 22:30:55",
    "download_certificate_url": "https://legallymail.com/emails/certificate/em_8f431b920ca7"
  }
}

{
  "id": "evt_1a98c7e42d31",
  "event": "signature.completed",
  "timestamp": 1729722420,
  "created_at": "2026-10-23T22:32:15+02:00",
  "api_version": "2026-01-01",
  "data": {
    "uuid": "sig_49f8021c-a81e-450b-bb99-35a9d821e901",
    "document_name": "Acuerdo_Confidencialidad_NDA.pdf",
    "status": "signed",
    "signed_at": "2026-10-23 22:32:15",
    "document_hash": "b2f0a1c9...",
    "signed_document_hash": "f183d02a...",
    "ts_confirmed_at": "2026-10-23 22:32:16",
    "download_signed_pdf_url": "https://legallymail.com/electronic-signature/download/sig_49f8021c-a81e-450b-bb99-35a9d821e901?signed=1",
    "download_acta_url": "https://legallymail.com/electronic-signature/download/sig_49f8021c-a81e-450b-bb99-35a9d821e901?acta=1"
  }
}

5. Código de Verificación de Firma (HMAC-SHA256)

<?php
$webhookSecret = 'whsec_tu_secreto_configurado';

$payload = file_get_contents('php://input');
$sigHeader = $_SERVER['HTTP_X_LEGALLYMAIL_SIGNATURE'] ?? '';

// Extraer t (timestamp) y v1 (hash)
preg_match('/t=(\d+),v1=([a-f0-9]+)/', $sigHeader, $matches);
if (count($matches) !== 3) {
    http_response_code(400);
    exit('Firma no proporcionada o formato inválido');
}

$timestamp = $matches[1];
$receivedHash = $matches[2];

// Proteger contra ataques de repetición (tolerancia de 5 minutos = 300 segundos)
if (abs(time() - $timestamp) > 300) {
    http_response_code(400);
    exit('Firma expirada (Timestamp fuera de tolerancia)');
}

// Calcular firma esperada
$expectedHash = hash_hmac('sha256', $timestamp . '.' . $payload, $webhookSecret);

if (!hash_equals($expectedHash, $receivedHash)) {
    http_response_code(401);
    exit('Firma criptográfica inválida');
}

// Firma verificada con éxito
$event = json_decode($payload, true);

switch ($event['event']) {
    case 'email.delivered':
        // Procesar entrega inmediata
        break;
    case 'email.opened':
        // Procesar lectura fehaciente
        break;
    case 'signature.completed':
        // Procesar documento firmado
        break;
}

http_response_code(200);
echo json_encode(['status' => 'received']);
const express = require('express');
const crypto = require('crypto');
const app = express();

const WEBHOOK_SECRET = 'whsec_tu_secreto_configurado';

app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
    const sigHeader = req.headers['x-legallymail-signature'];
    const rawBody = req.body.toString('utf8');

    if (!sigHeader) return res.status(400).send('Missing signature');

    const match = sigHeader.match(/t=(\d+),v1=([a-f0-9]+)/);
    if (!match) return res.status(400).send('Invalid signature format');

    const [_, timestamp, receivedHash] = match;

    // Tolerancia anti-replay: 5 minutos
    if (Math.abs(Date.now() / 1000 - timestamp) > 300) {
        return res.status(400).send('Timestamp expired');
    }

    const expectedHash = crypto
        .createHmac('sha256', WEBHOOK_SECRET)
        .update(`${timestamp}.${rawBody}`)
        .digest('hex');

    if (!crypto.timingSafeEqual(Buffer.from(expectedHash), Buffer.from(receivedHash))) {
        return res.status(401).send('Invalid signature');
    }

    const event = JSON.parse(rawBody);
    console.log(`Evento recibido: ${event.event}`, event.data);

    res.status(200).json({ received: true });
});

app.listen(3000);
import hmac
import hashlib
import time
import re
from fastapi import FastAPI, Request, HTTPException

app = FastAPI()
WEBHOOK_SECRET = "whsec_tu_secreto_configurado"

@app.post("/webhook")
async def handle_legallymail_webhook(request: Request):
    sig_header = request.headers.get("x-legallymail-signature")
    if not sig_header:
        raise HTTPException(status_code=400, detail="Missing signature")

    match = re.search(r"t=(\d+),v1=([a-f0-9]+)", sig_header)
    if not match:
        raise HTTPException(status_code=400, detail="Invalid signature header format")

    timestamp, received_hash = match.group(1), match.group(2)

    # Validar tolerancia de 5 minutos
    if abs(time.time() - int(timestamp)) > 300:
        raise HTTPException(status_code=400, detail="Timestamp expired")

    raw_body = await request.body()
    expected_hash = hmac.new(
        WEBHOOK_SECRET.encode('utf-8'),
        f"{timestamp}.".encode('utf-8') + raw_body,
        hashlib.sha256
    ).hexdigest()

    if not hmac.compare_digest(expected_hash, received_hash):
        raise HTTPException(status_code=401, detail="Invalid HMAC signature")

    event = await request.json()
    # Procesar event["event"]
    return {"status": "ok"}

6. Politica di Riprova e Idempotenza

  • Respuesta 2xx: Tu servidor debe responder con un código de estado HTTP 200 o 204 en un máximo de 5 segundos.
  • Reintentos automáticos: Si tu endpoint responde con un error 5xx, timeout o no es alcanzable, LegallyMail reintenta el envío de forma automática con retroceso exponencial (inmediato, +1m, +5m, +15m, +1h).
  • Idempotencia: Usa el identificador X-LegallyMail-Delivery para descartar eventos duplicados si tu servidor ya procesó la notificación anteriormente.

Codici di Errore

Codice Messaggio Descrizione
400 Bad Request I dati forniti non sono validi
401 Unauthorized Token non valido o non fornito
403 Forbidden Non hai il permesso per eseguire questa azione
Viene restituito anche quando l'utente non ha crediti/email rimanenti disponibili.
404 Not Found Risorsa non trovata. Verifica che la risorsa richiesta esista (ad esempio, un'email con il tracking_id specificato).
405 Method Not Allowed Metodo HTTP non consentito
429 Too Many Requests Troppe richieste. Limite di rate limiting superato.
500 Internal Server Error Errore interno del server

Limiti e Restrizioni

L'API ha le seguenti restrizioni:

  • Solo gli utenti con almeno un pagamento possono usare l'API
  • I limiti di email dipendono dal piano dell'utente
  • Si applicano limiti di velocità per proteggere il servizio
  • Limite di 10 richieste al minuto per indirizzo IP (errore 429 se superato)
  • api.documentation.limit_attachments
Escalabilidad de Rate Limiting: El límite base de 10 peticiones por minuto previene saturaciones accidentales. Si tu aplicación, ERP o CRM requiere una mayor cadencia de peticiones en horas punta, este límite se amplía progresivamente según la demanda de tu proyecto contactando con nuestro equipo de soporte.
Chat dal vivo Lukas
Caricamento chat...
Lukas (Agente di supporto) escribiendo...