# Webhooks

## Why use *Webhooks*?

When building integrations with Autentique, it can be helpful for your applications to receive events as they happen in your organizations.

To start receiving webhooks, you need to register your endpoints in the dashboard. After registration, Autentique can send real-time event data to your webhook endpoints whenever events occur in your organization. Autentique uses HTTPS to send these events as a JSON payload, which includes an Event object.

Receiving webhook events is especially useful for tracking asynchronous events, such as when a signer signs a document, when a document is completed, or when actions related to document processing are finished.

### Event Object

The following event shows an `update` to the name of a document.

{% code overflow="wrap" lineNumbers="true" fullWidth="false" %}

```json
{
  "id": "MXwyMWZiY2VjOS1lMWI1LTRkY2EtYWZiYi0wMjIwNjFlOWVhODg=",
  "object": "webhook",
  "name": "test endpoint 2",
  "format": "json",
  "url": "https://this-url-doesn't-exist.autentique.com.br/webhooks",
  "event": {
    "id": "21fbcec9-e1b5-4dca-afbb-022061e9ea88",
    "object": "event",
    "organization": 1,
    "type": "document.updated",
    "data": {
      "object": {
        "id": "89c7d2ab31f9f5a13b3d20ecf53319af387e54d240ae7be993",
        "name": "Updated name",
        "refusable": true,
        "stop_on_rejected": true,
        "qualified": false,
        "ignore_cpf": true,
        "sortable": false,
        "is_blocked": false,
        "sandbox": 0,
        "scrolling_required": 0,
        "locale": {
          "country": "BR",
          "language": "pt-BR",
          "timezone": "America/Sao_Paulo",
          "date_format": "d/m/Y"
        },
        "created_at": "2024-08-26T18:02:26.000000Z",
        "updated_at": "2024-08-26T18:03:27.000000Z",
        "deleted_at": null,
        "deadline_at": null,
        "lifecycle_in": "2029-08-26T03:00:00.000000Z",
        "email_template_id": null,
        "expiration_at": null,
        "notify_in": null,
        "reminder": null,
        "message": "I changed this message too",
        "reply_to": null,
        "signatures_count": 1,
        "signed_count": 0,
        "rejected_count": 0,
        "object": "document",
        "is_from_api": false,
        "signatures": [
          {
            "public_id": "7f25d72b-6155-11ef-9dae-0242ac170004",
            "name": "Felipe Autentique",
            "company": null,
            "email": "felipe@autentique.com.br",
            "phone": null,
            "cpf": "123.456.789-09",
            "birthday": "1979-08-13",
            "action": "Sign",
            "viewed": "2024-08-26T18:02:27.000000Z",
            "signed": null,
            "rejected": null,
            "validation_unapproved": null,
            "validation_approved": null,
            "validation_rejected": null,
            "created_at": "2024-08-26T18:02:26.000000Z"
          }
        ],
        "author": {
          "name": "Felipe Autentique",
          "company": null,
          "email": "felipe@autentique.com.br",
          "phone": null,
          "cpf": "123.456.789-09",
          "birthday": "1979-08-13"
        },
        "files": {
          "original": "https://storage.googleapis.com/f77/6e7a1fadeed9c56cf037b43a9cd0e6d1/o9baEgHHv1Tuf7AZttDqdL8vK68eUw56PaYXzRqV.original.pdf",
          "signed": "https://painel.autentique.com.br/documentos/89c7d2ab31f95a13b3d20ecf53319af387e54d240ae7be993/assinado.pdf"
        }
      },
      "previous_attributes": {
        "name": "teste",
        "refusable": false,
        "updated_at": "2024-08-26T18:02:26.000000Z",
        "message": "Please access and electronically sign the document by clicking the button above.",
        "ignore_cpf": false
      }
    },
    "created_at": "2024-08-26T18:03:27.387179Z"
  }
}
```

{% endcode %}

#### Type of Event

All webhooks sent are related to an event of a specific resource, which is indicated by the `type` field. Similarly, the `data.object` field corresponds to the resource of the event.

#### **Data Object and previous\_attributes**

For `*.updated` events, the event payload includes the `data.previous_attributes` field, which allows you to inspect what changed in the related resource. In the example above, the `document.updated` event shows that the document previously had the name "teste".

## How to Begin

1. Add your endpoint in the Autentique Developer Panel, selecting the events you want to listen to.
2. Set up an HTTPS function that accepts webhook requests with a POST method.
   1. Process POST requests with a JSON payload consisting of an event object.
   2. Quickly return a success status code (2xx) before any complex logic that may cause a timeout. For example, you need to return a 200 response before updating document data in your system.
   3. If desired, you can verify that events are sent by Autentique before performing any manipulation with the payload.

#### Examples of endpoint:

{% tabs %}
{% tab title="PHP" %}

```php
$payload = @file_get_contents('php://input');
$webhook = null;

//Before processing the payload

try {
    $webhook = json_decode($payload, true);
} catch(\UnexpectedValueException $e) {
    // Invalid payload
    http_response_code(400);
    exit();
}

$event = $webhook['event'];

// Process the event
switch ($event['type']) {
    case 'document.created':
        $document = $event['data']['object']; // Contains a document object,
        // define a method to process the document
        // handleDocumentCreated($document);
        break;
    case 'signature.accepted':
        $signature = $event['data']['object']; // Contains a signature object
        // Define a method to process the signature
        // handleSignatureAccepted($signature);
        break;
        // ... process other types of events
    default:
        echo 'Received unknown event type '.$event['type'];
}

http_response_code(200);
```

{% endtab %}

{% tab title="Go" %}

```go
package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
)

type WebhookPayload struct {
	ID     string `json:"id"`
	Object string `json:"object"`
	Name   string `json:"name"`
	Format string `json:"format"`
	URL    string `json:"url"`
	Event  Event  `json:"event"`
}

type Event struct {
	ID           string                 `json:"id"`
	Object       string                 `json:"object"`
	Organization int                    `json:"organization"`
	Type         string                 `json:"type"`
	Data         map[string]interface{} `json:"data"`
	CreatedAt    string                 `json:"created_at"`
}

func webhookHandler(w http.ResponseWriter, r *http.Request) {
	payload, err := ioutil.ReadAll(r.Body)
	if err != nil {
		http.Error(w, "Invalid payload", http.StatusBadRequest)
		return
	}

	var webhook WebhookPayload
	if err := json.Unmarshal(payload, &webhook); err != nil {
		http.Error(w, "Invalid payload", http.StatusBadRequest)
		return
	}

	event := webhook.Event
	eventData := event.Data
	eventObject := eventData["object"].(map[string]interface{})

	switch event.Type {
	case "document.created":
		// handleDocumentCreated(eventObject)
		fmt.Println("Document created:", eventObject)
	case "document.updated":
		// handleDocumentUpdated(eventObject)
		fmt.Println("Document updated:", eventObject)
	case "signature.accepted":
		// handleSignatureAccepted(eventObject)
		fmt.Println("Signature accepted:", eventObject)
	default:
		fmt.Printf("Received unknown event type: %s\n", event.Type)
	}

	w.WriteHeader(http.StatusOK)
}

func main() {
	http.HandleFunc("/webhook", webhookHandler)
	http.ListenAndServe(":8080", nil)
}
```

{% endtab %}

{% tab title="Java" %}

```java
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.http.ResponseEntity;
import org.springframework.http.HttpStatus;

import java.util.Map;

@RestController
@RequestMapping("/webhook")
public class WebhookController {

    @PostMapping
    public ResponseEntity<String> handleWebhook(@RequestBody Map<String, Object> payload) {
        try {
            Map<String, Object> event = (Map<String, Object>) payload.get("event");
            String eventType = (String) event.get("type");
            Map<String, Object> data = (Map<String, Object>) event.get("data");
            Map<String, Object> eventObject = (Map<String, Object>) data.get("object");

            switch (eventType) {
                case "document.created":
                    // handleDocumentCreated(eventObject);
                    System.out.println("Document created: " + eventObject);
                    break;
                case "document.updated":
                    // handleDocumentUpdated(eventObject);
                    System.out.println("Document updated: " + eventObject);
                    break;
                case "signature.accepted":
                    // handleSignatureAccepted(eventObject);
                    System.out.println("Signature accepted: " + eventObject);
                    break;
                default:
                    System.out.println("Received unknown event type: " + eventType);
            }

            return new ResponseEntity<>("Webhook received", HttpStatus.OK);
        } catch (Exception e) {
            return new ResponseEntity<>("Invalid payload", HttpStatus.BAD_REQUEST);
        }
    }
}
```

{% endtab %}

{% tab title="NodeJS" %}

```javascript
const express = require('express');
const bodyParser = require('body-parser');

const app = express();
app.use(bodyParser.json());

app.post('/webhook', (req, res) => {
    try {
        const payload = req.body;
        const event = payload.event;
        const eventType = event.type;
        const eventData = event.data;
        const eventObject = eventData.object;

        switch (eventType) {
            case 'document.created':
                // handleDocumentCreated(eventObject);
                console.log("Document created:", eventObject);
                break;
            case 'document.updated':
                // handleDocumentUpdated(eventObject);
                console.log("Document updated:", eventObject);
                break;
            case 'signature.accepted':
                // handleSignatureAccepted(eventObject);
                console.log("Signature accepted:", eventObject);
                break;
            default:
                console.log(`Received unknown event type: ${eventType}`);
        }

        res.status(200).send('Webhook received');
    } catch (error) {
        res.status(400).send('Invalid payload');
    }
});

app.listen(8080, () => {
    console.log('Server is running on port 8080');
});
```

{% endtab %}

{% tab title="Python" %}

```python
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/webhook', methods=['POST'])
def webhook():
    try:
        payload = request.json
        event = payload['event']
        event_type = event['type']
        event_data = event['data']
        event_object = event_data['object']

        if event_type == 'document.created':
            # handle_document_created(event_object)
            print("Document created:", event_object)
        elif event_type == 'document.updated':
            # handle_document_updated(event_object)
            print("Document updated:", event_object)
        elif event_type == 'signature.accepted':
            # handle_signature_accepted(event_object)
            print("Signature accepted:", event_object)
        else:
            print(f"Received unknown event type: {event_type}")

        return jsonify({"status": "success"}), 200
    except Exception as e:
        return jsonify({"error": "Invalid payload"}), 400

if __name__ == '__main__':
    app.run(port=8080)
```

{% endtab %}

{% tab title="Ruby" %}

```ruby
class WebhooksController < ApplicationController
    skip_before_action :verify_authenticity_token

    def webhook
        begin
            payload = JSON.parse(request.body.read)
            event = payload['event']
            event_type = event['type']
            event_data = event['data']
            event_object = event_data['object']

            case event_type
            when 'document.created'
                # handle_document_created(event_object)
                puts "Document created: #{event_object}"
            when 'document.updated'
                # handle_document_updated(event_object)
                puts "Document updated: #{event_object}"
            when 'signature.accepted'
                # handle_signature_accepted(event_object)
                puts "Signature accepted: #{event_object}"
            else
                puts "Received unknown event type: #{event_type}"
            end

            render json: { status: 'success' }, status: :ok
        rescue => e
            render json: { error: 'Invalid payload' }, status: :bad_request
        end
    end
end
```

{% endtab %}
{% endtabs %}

### Event Order: <a href="#event-ordering" id="event-ordering"></a>

Autentique does not guarantee the delivery of events in the order they were generated. For example, the creation of a document may generate the following events:

* `document.updated`
* `document.created`
* `signature.created`
* `signature.viewed` (if the author is a signer and in the document creation)&#x20;

Your endpoint should not expect the delivery of these events in this order and should process them accordingly. You can also use the API to retrieve missing objects (for example, you can get signatures, folders, and organizations using the information from `document.created` if you receive that event first).

### Best practices for Webhook usage

Review these best practices to ensure that your webhooks remain secure and work well with your integration.

**Quickly Return a 2xx Response**

The endpoint must quickly return a success status code (2xx) before any complex logic that could cause a timeout. For example, when receiving a `document.finished` event, you need to return a 200 response before updating the document data in your system.

**Manage Events Asynchronously**

Set up the handler to process received events with an asynchronous queue. Synchronously processing events can cause scalability issues, especially during peak webhook delivery times.

* **Using Queues**: Use asynchronous queues to process events concurrently at a rate your system can handle.

**Handle Duplicate Events**

Occasionally, webhook endpoints may receive the same event more than once. To protect against receiving duplicate events:

* **Store Event IDs**: Keep track of the event IDs you have processed and ignore already registered events.
* **Duplicate Identification**: In some cases, two webhooks of the same event may be sent to your endpoint. Use the object ID in `event.data` along with the `event.type`.

**Listen Only to Required Event Types**

Configure your webhook endpoints to receive only the event types required by your integration. Listening for additional or all events can overload your server and is not recommended.

* **Event Configuration**: You can change the events that a webhook endpoint receives in the Dashboard.

**CSRF Protection-Free Webhook Route**

If you're using frameworks like Rails, Django, or Laravel, your site may automatically check if each POST request contains a CSRF token. This is an important security feature that protects against cross-site request forgery attacks but may prevent your site from processing legitimate webhook events. To resolve this, you can exempt the webhook route from CSRF protection.

{% tabs %}
{% tab title="Laravel" %}

```php
<?php

use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Middleware;

# On Laravel, 
# you can add the webhook route as an exception
# middleware VerifyCsrfToken on the file `bootstrap/app.php`
return Application::configure(basePath: dirname(__DIR__))
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
        commands: __DIR__.'/../routes/console.php',
        channels: __DIR__.'/../routes/channels.php',
        health: '/up',
    )
    ->withMiddleware(function (Middleware $middleware) {
        $middleware->validateCsrfTokens(except: [
            'webhooks/*' // <-- remove the validation route
        ]);
    })->create();
```

{% endtab %}

{% tab title="Django" %}

```python
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse

# No Django, você pode isentar uma view específica 
# da verificação CSRF sando o decorator @csrf_exempt
@csrf_exempt
@require_POST
def webhook_endpoint(request):
    # Processar o webhook
```

{% endtab %}

{% tab title="Rails" %}

```ruby
# No Rails, você pode isentar uma ação específica do protect_from_forgery
class WebhooksController < ApplicationController
  protect_from_forgery except: :webhook

  def webhook
    # Process webhook data in `params`
  end

end
```

{% endtab %}
{% endtabs %}

**Verify if Events Are Sent by Autentique**

To ensure that the events you receive truly come from Autentique, it's crucial to validate the HMAC signatures present in the webhook headers. Here are some examples of how to do this:

{% tabs %}
{% tab title="PHP" %}

```php
public function verifySignature(array $headers, string $payload, string $secret): bool
{
    if (!isset($headers['x-autentique-signature'])) {
        return false;
    }

    $signature = $headers['x-autentique-signature'];
    $calculatedSignature = hash_hmac('sha256', $payload, $secret);

    return hash_equals($calculatedSignature, $signature);
}
```

{% endtab %}

{% tab title="Node" %}

```javascript
const crypto = require('crypto');

function verifySignature(headers, rawBody, secret) {
  const signature = headers["x-autentique-signature"];
  if (!signature) {
    return false;
  }
  const calculatedSignature = crypto
    .createHmac("sha256", secret)
    .update(rawBody)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(calculatedSignature, "hex"),
    Buffer.from(signature, "hex"),
  );
}
```

{% endtab %}

{% tab title="Python" %}

```python
import hmac
import hashlib
import json

def verify_signature(headers, payload, secret):
    signature = headers.get('X-Autentique-Signature')
    if not signature:
        return False
    payload_json = json.dumps(payload, separators=(',', ':')).encode('utf-8')
    calculated_signature = hmac.new(secret.encode('utf-8'), payload_json, hashlib.sha256).hexdigest()
    return hmac.compare_digest(calculated_signature, signature)
```

{% endtab %}

{% tab title="Ruby" %}

```ruby
require 'json'
require 'openssl'
require 'active_support/security_utils'

def verify_signature(headers, payload, secret)
  signature = headers['X-Autentique-Signature']
  return false unless signature
  payload_json = payload.to_json
  calculated_signature = OpenSSL::HMAC.hexdigest('SHA256', secret, payload_json)
  ActiveSupport::SecurityUtils.secure_compare(calculated_signature, signature)
end
```

{% endtab %}

{% tab title="Java" %}

```java
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;

public class WebhookVerifier {
    private String secret;

    public WebhookVerifier(String secret) {
        this.secret = secret;
    }

    public boolean verifySignature(String headerSignature, String payload) throws Exception {
        String calculatedSignature = calculateSignature(payload);
        return MessageDigest.isEqual(calculatedSignature.getBytes(StandardCharsets.UTF_8),
                                     headerSignature.getBytes(StandardCharsets.UTF_8));
    }

    private String calculateSignature(String payload) throws Exception {
        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
        sha256_HMAC.init(secret_key);
        byte[] hash = sha256_HMAC.doFinal(payload.getBytes(StandardCharsets.UTF_8));
        return bytesToHex(hash);
    }

    private String bytesToHex(byte[] bytes) {
        StringBuilder hexString = new StringBuilder(2 * bytes.length);
        for (byte b : bytes) {
            String hex = Integer.toHexString(0xff & b);
            if(hex.length() == 1) hexString.append('0');
            hexString.append(hex);
        }
        return hexString.toString();
    }
}

```

{% endtab %}
{% endtabs %}

### Document Object

```json
{
  "id": "MXwwNmMzOGYyMS0zNjhjLTQyNmItOTM2Ny1iMzNhNzQ2NmY5MGM=",
  "object": "webhook",
  "name": "docs",
  "format": "json",
  "url": "https://this-url-does't-exist.autentique.com.br/webhooks",
  "event": {
    "id": "06c38f21-368c-426b-9367-b33a7466f90c",
    "object": "event",
    "organization": 1,
    "type": "document.updated",
    "data": {
      "id": "1cf7d351a96696fdf450ba893f6720463599dd8c34e0aeda803d",
      "object": "document",
      "name": "test sddd",
      "message": "Please access and electronically sign the document by clicking the button above.",
      "refusable": true,
      "author": {
        "name": "Felipe Autentique",
        "company": null,
        "email": "felipe@autentique.com.br",
        "phone": null,
        "cpf": "03351152094",
        "birthday": "2005-01-24"
      },
      "signatures": [
        {
          "public_id": "92b49dbe-df08-11ef-903c-0242ac140004",
          "object": "signature",
          "user": {
            "name": "Felipe Autentique",
            "company": null,
            "email": "felipe@autentique.com.br",
            "phone": null,
            "cpf": "03351152094",
            "birthday": "2005-01-24"
          },
          "document": "1cf7d351a96696450ba893f6720463599dd8c34e0aeda803d",
          "action": "Sign",
          "viewed": "2025-01-30T12:49:01.000000Z",
          "signed": null,
          "rejected": null,
          "biometric_unapproved": null,
          "biometric_approved": null,
          "biometric_rejected": null,
          "events": [],
          "created_at": "2025-01-30T12:48:58.000000Z"
        },
        {
          "public_id": "93115640-df08-11ef-903c-0242ac140004",
          "object": "signature",
          "user": {
            "name": "Mateus Autentique",
            "company": null,
            "email": "mateus@autentique.com.br",
            "phone": null,
            "cpf": "189.614.966-03",
            "birthday": "1979-04-19"
          },
          "document": "1cf7d351a96696450ba893f6720463599dd8c34e0aeda803d",
          "action": "Sign",
          "viewed": null,
          "signed": null,
          "rejected": null,
          "biometric_unapproved": null,
          "biometric_approved": null,
          "biometric_rejected": null,
          "events": [],
          "created_at": "2025-01-30T12:48:59.000000Z"
        }
      ],
      "stop_on_rejected": true,
      "qualified": false,
      "ignore_cpf": false,
      "sortable": false,
      "is_blocked": false,
      "sandbox": false,
      "api": false,
      "scrolling_required": false,
      "locale": {
        "country": "BR",
        "language": "pt-BR",
        "timezone": "America/Sao_Paulo",
        "date_format": "d/m/Y"
      },
      "email_template_id": null,
      "expiration_at": null,
      "notify_in": null,
      "reminder": null,
      "reply_to": null,
      "signatures_count": 2,
      "signed_count": 0,
      "rejected_count": 0,
      "files": {
        "original": "https://storage.googleapis.com/f77-dev/9be6ab49679f844230682d7335230029/3jTmKFWps4mIaMByzrj7zmFywm4a6LYIjUDfNhZ7.original.pdf",
        "signed": "https://painel.autentique.com.br/documentos/1cf7d351a96696450ba893f6720463599dd8c34e0aeda803d/assinado.pdf"
      },
      "created_at": "2025-01-30T12:48:58.000000Z",
      "updated_at": "2025-01-30T12:49:56.000000Z",
      "deleted_at": null,
      "deadline_at": null,
      "lifecycle_in": "2030-01-30T03:00:00.000000Z"
    },
    "previous_attributes": {
      "signatures_count": 3
    },
    "created_at": "2025-01-30T12:49:56.342822Z"
  }
}
```

### Signature Object

```json
{
  "id": "MjV8OTQ4OGNlMTEtNzBiZi00ZGI3LTg1OGItZDc2ZjFkZDI5MGRj",
  "object": "webhook",
  "name": "sig",
  "format": "json",
  "url": "https://this-url-does't-exist.autentique.com.br/webhooks",
  "event": {
    "id": "9488ce11-70bf-4db7-858b-d76f1dd290dc",
    "object": "event",
    "organization": 1519203,
    "type": "signature.accepted",
    "data": {
      "public_id": "f8911dcd-dfcd-11ef-9465-42010a2b610e",
      "object": "signature",
      "user": {
        "name": "Felipe Autentique",
        "company": null,
        "email": "felipe@autentique.com",
        "phone": null,
        "cpf": "03351152094",
        "birthday": "2002-03-04"
      },
      "mail": {
        "sent": "2025-04-09 09:21:35",
        "opened": "2025-04-09 09:25:35",
        "refused": null,
        "delivered": null,
        "reason": null
      },
      "document": "f48a8b465d02dd87559e08f06c41e3b6d548c4d7ad835eb0f",
      "action": "Sign",
      "viewed": "2025-01-31T12:22:01.000000Z",
      "signed": "2025-01-31T12:22:30.000000Z",
      "rejected": null,
      "biometric_unapproved": null,
      "biometric_approved": null,
      "biometric_rejected": null,
      "events": [
        {
          "type": "viewed", // Can be: viewed, accepted, rejected, biometric_approved, biometric_rejected, biometric_unapproved
          "document": "f48a8b465d02dd87559e08f06c41e3b6d548c4d7ad835eb0f",
          "user": {
            "uuid": "ebcca9bc391a60336e777a23d32ada4410fe8b",
            "name": null,
            "email": "felipe@autentique.com",
            "cpf": null,
            "birthday": null
          },
          "geolocation": {
            "country": "Brazil",
            "countryISO": "BR",
            "state": "Rio Grande do Sul",
            "stateISO": "RS",
            "city": "Erechim",
            "zipcode": "99704094",
            "latitude": -27.6767,
            "longitude": -52.2559
          },
          "reason": null,
          "ip": "192.168.65.1",
          "port": 29317,
          "created_at": "2025-03-18T16:22:49.000000Z"
        },
        ...
      ]
      "created_at": "2025-01-31T12:22:00.000000Z"
    },
    "previous_attributes": [],
    "created_at": "2025-01-31T12:22:30.495056Z"
  }
}
```

### Member Object

```json
{
  "id": "MjZ8NzY1MjY1NTAtNzg3YS00YjU3LTk2MWYtN2EwMThlOTFkOGRj",
  "object": "webhook",
  "name": "memb",
  "format": "json",
  "url": "https://this-url-does't-exist.autentique.com.br/webhooks",
  "event": {
    "id": "76526550-787a-4b57-961f-7a018e91d8dc",
    "object": "event",
    "organization": 1519203,
    "type": "member.created",
    "data": {
      "user": {
        "name": "Felipe Autentique",
        "company": null,
        "email": "felipe@autentique.com.br",
        "phone": null,
        "cpf": "03351152094",
        "birthday": "2002-03-04"
      },
      "group": {
        "uuid": "96d50d5a-b69c-4c84-b2d9-3f37c227cebf",
        "name": "Administrador",
        "organization": 1519203,
        "permissions": {
          "overwrite_permissions": true,
          "create_documents": true,
          "sign_documents": true,
          "delete_documents": true,
          "archive_documents": true,
          "view_documents_gr": true,
          "view_folders_gr": true,
          "actions_folders_gr": true,
          "actions_documents_gr": true,
          "actions_templates_gr": true,
          "actions_members_oz": true,
          "actions_groups_oz": true,
          "actions_webhooks_oz": true,
          "view_documents_oz": true,
          "view_member_documents_oz": true,
          "view_group_documents_oz": true,
          "view_folders_oz": true,
          "view_member_folders_oz": true,
          "view_group_folders_oz": true,
          "actions_documents_oz": true,
          "view_invoices_oz": true,
          "change_plan_oz": true,
          "actions_folders_oz": true,
          "change_appearances_oz": true,
          "change_whitelabel_oz": false,
          "enterprise_access": false
        },
        "configs": {
          "geral": false,
          "sobrescrever_modelos": false,
          "overwrite_template_group": false,
          "sobrescrever_organizacao": false
        }
      },
      "permissions": {
        "overwrite_permissions": false,
        "create_documents": true,
        "sign_documents": true,
        "delete_documents": true,
        "archive_documents": true,
        "view_documents_gr": true,
        "view_folders_gr": true,
        "actions_folders_gr": false,
        "actions_documents_gr": false,
        "actions_templates_gr": false,
        "actions_members_oz": false,
        "actions_groups_oz": false,
        "actions_webhooks_oz": false,
        "view_documents_oz": true,
        "view_member_documents_oz": false,
        "view_group_documents_oz": false,
        "view_folders_oz": true,
        "view_member_folders_oz": false,
        "view_group_folders_oz": false,
        "actions_documents_oz": false,
        "view_invoices_oz": true,
        "change_plan_oz": false,
        "actions_folders_oz": false,
        "change_appearances_oz": false,
        "change_whitelabel_oz": false,
        "enterprise_access": false
      }
    },
    "previous_attributes": [],
    "created_at": "2025-01-31T12:24:48.477885Z"
  }
}
```

## Types of events

Autentique's API uses specific events triggered when certain actions occur.

\
All webhooks sent are related to an event of a specific resource, defined by the "type" field. The content of the webhooks only varies between different event types.

These events are classified into three main categories

* **Document**: Events related to the creation, modification, and completion of documents.
* **Signature**: Events related to the signature flow in documents.
* **Member**: Events related to the members of the organization.

### Document Events

<table><thead><tr><th width="279">Event</th><th>Description</th></tr></thead><tbody><tr><td>document.created</td><td>Triggered when a new document is created in the application.</td></tr><tr><td>document.updated</td><td>Triggered when an existing document is updated or edited (e.g., changes to additional settings).</td></tr><tr><td>document.deleted</td><td>Triggered when a document is permanently deleted from the application.</td></tr><tr><td>document.finished</td><td>Triggered when all signatures or steps associated with the document are successfully completed.</td></tr></tbody></table>

### Signature Events

<table><thead><tr><th width="276">Event</th><th>Description</th></tr></thead><tbody><tr><td>signature.created</td><td>Triggered when a new signature request is created for a signer in a document.</td></tr><tr><td>signature.updated</td><td>Triggered when there is a change or update to the signature request, such as a change in conditions or the signature status (e.g., placing an invisible signature).</td></tr><tr><td>signature.deleted</td><td>Triggered when a pending signer is removed from the document.</td></tr><tr><td>signature.viewed</td><td>Triggered when the signer views the document for the first time, indicating they accessed the content but have not taken any further action.</td></tr><tr><td>signature.accepted</td><td>Triggered when the signer successfully completes the signature process.</td></tr><tr><td>signature.rejected</td><td>Triggered when the signer explicitly declines the signature.</td></tr><tr><td>signature.biometric_approved</td><td>Triggered when the signer's biometric verification is successfully validated by the document creator or through automatic validation.</td></tr><tr><td>signature.biometric_unapproved</td><td>Triggered when a document's signature has pending checks, such as manual validation that remains in an approval state and needs to be completed by the document owner.</td></tr><tr><td>signature.biometric_reset</td><td>Triggered when a manual approval is rejected while the "Request new validation upon rejection" parameter is active.</td></tr><tr><td>signature.biometric_rejected</td><td>Triggered when the biometric verification is rejected by the document author.</td></tr><tr><td>signature.delivery_failed</td><td>Triggered when an error occurs while sending an email to the signer.</td></tr></tbody></table>

### Member Events

<table><thead><tr><th width="279">Event</th><th>Description</th></tr></thead><tbody><tr><td>member.created</td><td>Triggered when a new member joins the organization</td></tr><tr><td>member.deleted</td><td>Triggered when a member is removed from the organization</td></tr></tbody></table>

## *Webhooks* that failed

When a webhook is sent but not successfully received by the registered endpoint, it is added to the list of undelivered webhooks in the error log, available on our [webhook endpoint registration page.](https://app.gitbook.com/s/2qaCIHP2IAUGdu6Nkg7x/)

Webhooks on this list will undergo up to three additional delivery attempts, made after 60, 120, and 300 seconds. If all attempts fail, the event will remain on the list for up to 14 days.
