> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tiro.ooo/llms.txt
> Use this file to discover all available pages before exploring further.

# Webhooks Overview

> A webhook endpoint is a URL in your app where Tiro sends an HTTP POST the moment a note, summary, or document changes — real-time events, no polling.

export const EventBaseStructure = {
  id: "evt_01J9ABCDEF",
  type: "note.created",
  createdAt: "2025-09-05T07:12:34Z",
  workspaceGuid: "ws_a1b2c3d4",
  data: {
    resourceType: "Note",
    resourceId: "note-guid-123",
    resource: {
      "title": "Meeting Note"
    }
  }
};

export const Json = ({data, lang = 'json', spaces = 2}) => <pre>
    <code className={`language-${lang}`}>{JSON.stringify(data, null, spaces)}</code>
  </pre>;

## What are Webhooks?

Webhooks allow your application to receive real-time notifications when events occur in your workspace. Instead of polling the API, Tiro sends HTTP POST requests to your endpoint whenever something happens.

## How Webhooks Work

1. **Configure**: Set up a webhook endpoint in your application
2. **Register**: Add your endpoint to receive the events you care about
3. **Receive**: Get instant notifications when events occur
4. **Process**: Handle the event data in your application

## Event & Resource Structure

Webhooks are organized around **Events** and **Resources**:

### Events

Events represent actions that occur in your workspace. See [Note Events](/en/developers/webhooks/events/note-events), [Note Document Events](/en/developers/webhooks/events/note-document-events), [Note Summary Events](/en/developers/webhooks/events/note-summary-events), and [FolderNote Events](/en/developers/webhooks/events/folder-note-events) for detailed information.
Webhook events are designed to stay under several hundred KB by including only metadata. Large content like transcripts and scripts are accessed via separate APIs to ensure webhook reliability and performance.

### Resources

Resources represent the main entities that events can act upon. Currently supported:

* `Note`: Individual note resources
* `NoteDocument`: Template-based documents generated from notes
* `NoteSummary`: AI-generated summaries for notes
* `FolderNoteRelation`: Relationship between folders and notes

## Webhook Payload Structure

All webhook events follow the standard [Event Structure](/en/developers/webhooks/events/schema) structure:

<Json data={EventBaseStructure} />

## Security

Webhook requests are authenticated using your secret key in the Authorization header:

```
Authorization: Bearer {secret_key}
```

The secret key is provided when you configure your webhook endpoint and is used to verify the authenticity of incoming requests. Simply compare the provided secret with your configured secret to verify authenticity.

### Verification Example

<CodeGroup>
  ```javascript Node.js theme={null}
  function verifyWebhookAuth(authHeader, expectedSecret) {
    const token = authHeader.replace('Bearer ', '');
    return token === expectedSecret;
  }

  // Usage in your webhook endpoint
  app.post('/en/developers/webhooks/tiro', (req, res) => {
    const authHeader = req.headers.authorization;
    
    if (!verifyWebhookAuth(authHeader, process.env.WEBHOOK_SECRET)) {
      return res.status(401).send('Unauthorized');
    }
    
    // Process webhook event
    const event = req.body;
    handleWebhookEvent(event);
    
    res.status(200).send('OK');
  });
  ```

  ```python Python theme={null}
  import os
  from flask import Flask, request

  def verify_webhook_auth(auth_header, expected_secret):
      if not auth_header or not auth_header.startswith('Bearer '):
          return False
      token = auth_header.replace('Bearer ', '')
      return token == expected_secret

  # Usage in your webhook endpoint
  @app.route('/en/developers/webhooks/tiro', methods=['POST'])
  def handle_webhook():
      auth_header = request.headers.get('Authorization')
      
      if not verify_webhook_auth(auth_header, os.getenv('WEBHOOK_SECRET')):
          return 'Unauthorized', 401
      
      # Process webhook event
      event = request.get_json()
      handle_webhook_event(event)
      
      return 'OK', 200
  ```

  ```go Go theme={null}
  import (
      "encoding/json"
      "net/http"
      "os"
      "strings"
  )

  func verifyWebhookAuth(authHeader, expectedSecret string) bool {
      if !strings.HasPrefix(authHeader, "Bearer ") {
          return false
      }
      token := strings.TrimPrefix(authHeader, "Bearer ")
      return token == expectedSecret
  }

  // Usage in your webhook endpoint
  func handleWebhook(w http.ResponseWriter, r *http.Request) {
      authHeader := r.Header.Get("Authorization")
      
      if !verifyWebhookAuth(authHeader, os.Getenv("WEBHOOK_SECRET")) {
          http.Error(w, "Unauthorized", http.StatusUnauthorized)
          return
      }
      
      // Process webhook event
      var event WebhookEvent
      json.NewDecoder(r.Body).Decode(&event)
      handleWebhookEvent(event)
      
      w.WriteHeader(http.StatusOK)
  }
  ```

  ```kotlin Kotlin + Spring theme={null}
  @RestController
  class WebhookController {
      
      @Value("\${webhook.secret}")
      private lateinit var webhookSecret: String
      
      private fun verifyWebhookAuth(authHeader: String?, expectedSecret: String): Boolean {
          if (authHeader?.startsWith("Bearer ") != true) {
              return false
          }
          val token = authHeader.removePrefix("Bearer ")
          return token == expectedSecret
      }
      
      // Usage in your webhook endpoint
      @PostMapping("/en/developers/webhooks/tiro")
      fun handleWebhook(
          @RequestHeader("Authorization") authHeader: String?,
          @RequestBody event: WebhookEvent
      ): ResponseEntity<String> {
          if (!verifyWebhookAuth(authHeader, webhookSecret)) {
              return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("Unauthorized")
          }
          
          // Process webhook event
          handleWebhookEvent(event)
          return ResponseEntity.ok("OK")
      }
  }
  ```
</CodeGroup>

## Delivery & Retries

* **Method**: HTTP POST
* **Content-Type**: `application/json`
* **Timeout**: 60 seconds
* **Retries**: Up to 5 retries (6 total attempts) with exponential backoff
* **Success**: Any 2xx HTTP status code

### Retry Schedule

1. 15 seconds
2. 30 seconds
3. 5 minutes
4. 30 minutes
5. 2 hours

## Getting Started

1. **Set up your endpoint**: Create an HTTP endpoint that can receive POST requests
2. **Configure webhooks**: Register your webhook endpoint in [Tiro Platform](https://platform.tiro.ooo)
3. **Handle events**: Process incoming webhook payloads in your application

Configure your webhook endpoint at [Tiro Platform](https://platform.tiro.ooo).

<Note>
  Webhook endpoints are registered and managed per workspace. Add an endpoint in Platform and it receives events from the current workspace. If you operate several workspaces, register an endpoint for each one and use the top-level `workspaceGuid` in the payload to tell which workspace an event came from.
</Note>

With your endpoint registered, get familiar with the [Event Structure](/en/developers/webhooks/events/schema) you'll receive — then read [Best Practices](/en/developers/webhooks/best-practices) to handle retries, idempotency, and signature verification before you go live.
