Skip to main content

Events API

Overview

The Events API provides a powerful pub/sub system for subscribing serverless functions to system events. Functions can be triggered automatically when specific events occur in the platform, enabling reactive workflows, data validation, and custom business logic.

Key Features:

  • Event-driven architecture - Subscribe functions to system events
  • Async execution - All functions execute asynchronously asynchronously
  • Blocking support - PRE events can block operations until completion
  • Type-safe - Pydantic validation for event types and parameters
  • Multi-tenant - Fully integrated with tenant isolation

Architecture

Key Concepts

EventType Enum

Events are defined as an enum to ensure type safety:

from core.events.registry import EventType

class EventType(str, Enum):
PRE_USER_CREATE = "PRE_USER_CREATE"
POST_USER_CREATE = "POST_USER_CREATE"

Event Object

Events use Pydantic models for validation:

from core.events.registry import Event, EventType

event = Event(
event_type=EventType.POST_USER_CREATE,
params={
"user_id": 123,
"username": "john_doe",
"email": "john@example.com"
}
)

Subscribers

A Subscriber links a Function to an Event. When the event is published, all subscribed functions are executed.

Unique Constraint: Each (event, function) pair must be unique - a function can only subscribe once to each event type.

Async Execution

All functions execute asynchronously asynchronously. The publish_event() method returns task IDs immediately without waiting for completion (unless explicitly blocked).

PRE vs POST Events

PRE Events (e.g., PRE_USER_CREATE):

  • Fire before the main operation
  • Can block execution by waiting for all tasks to complete
  • Used for validation, enrichment, or prerequisite checks
  • If any task fails, the entire operation is aborted

POST Events (e.g., POST_USER_CREATE):

  • Fire after the main operation completes
  • Non-blocking (fire and forget)
  • Used for notifications, logging, analytics, or side effects
  • Errors are logged but don't affect the main operation

Event Registry

Available Events

Event TypeWhen TriggeredBlockingUse Cases
PRE_USER_CREATEBefore user.save()YesValidate user data, check external systems, enrich attributes
POST_USER_CREATEAfter user.save()NoSend welcome email, create default settings, log analytics

Base URL Structure

Global Events:

/api/cloud/events/

App-Scoped Events:

/api/apps/{app_slug}/events/

API Endpoints

List All Events

Get a list of all available event types.

Endpoint:

GET /api/cloud/events/

Authentication: Required

Response:

{
"success": true,
"message": "Events retrieved successfully",
"data": {
"events": [
"PRE_USER_CREATE",
"POST_USER_CREATE"
]
}
}

Example:

curl -X GET https://your-site.taruvi.cloud/api/cloud/events/ \
-H "Authorization: Bearer YOUR_TOKEN"

Response (200 OK):

{
"success": true,
"message": "Events retrieved successfully",
"data": {
"events": [
"PRE_USER_CREATE",
"POST_USER_CREATE"
]
}
}

Subscribe to Event

Subscribe a function to an event type.

Endpoint:

POST /api/cloud/events/{event_name}/subscribe/

Authentication: Required

Path Parameters:

  • event_name (string, required) - Event type name (e.g., "PRE_USER_CREATE")

Request Body:

{
"function_id": 123
}

Response:

{
"success": true,
"message": "Function 123 subscribed to event 'PRE_USER_CREATE' successfully",
"data": {
"id": 1,
"event": "PRE_USER_CREATE",
"function": 123,
"function_name": "Validate User Data",
"function_slug": "validate-user-data",
"app_name": "User Management",
"app_slug": "user-management",
"created_at": "2025-01-15T10:30:00Z",
"updated_at": "2025-01-15T10:30:00Z"
}
}

Error Responses:

  • 400 Bad Request - Invalid event type
{
"success": false,
"message": "Invalid event type 'INVALID_EVENT'. Must be one of: PRE_USER_CREATE, POST_USER_CREATE"
}
  • 400 Bad Request - Function already subscribed
{
"success": false,
"message": "Function 123 is already subscribed to event 'PRE_USER_CREATE'"
}
  • 400 Bad Request - Function doesn't exist
{
"success": false,
"message": "Function with id 123 does not exist"
}

Example:

curl -X POST https://your-site.taruvi.cloud/api/cloud/events/PRE_USER_CREATE/subscribe/ \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"function_id": 123}'

Response (200 OK):

{
"success": true,
"message": "Function 123 subscribed to event 'PRE_USER_CREATE' successfully",
"data": {
"id": 1,
"event": "PRE_USER_CREATE",
"function": 123,
"function_name": "Validate User Data",
"function_slug": "validate-user-data"
}
}

Unsubscribe from Event

Unsubscribe a function from an event type.

Endpoint:

POST /api/cloud/events/{event_name}/unsubscribe/

Authentication: Required

Path Parameters:

  • event_name (string, required) - Event type name

Request Body:

{
"function_id": 123
}

Response:

{
"success": true,
"message": "Function 123 unsubscribed from event 'PRE_USER_CREATE' successfully",
"data": {}
}

Error Responses:

  • 404 Not Found - Subscription doesn't exist
{
"success": false,
"message": "Function 123 was not subscribed to event 'PRE_USER_CREATE'"
}

Example:

curl -X POST https://your-site.taruvi.cloud/api/cloud/events/PRE_USER_CREATE/unsubscribe/ \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"function_id": 123}'

Response (200 OK):

{
"success": true,
"message": "Function 123 unsubscribed from event 'PRE_USER_CREATE' successfully",
"data": {}
}

Get App Event Details

Get event details with subscribed functions filtered by app.

Endpoint:

GET /api/apps/{app_slug}/events/{event_name}/

Authentication: Required

Path Parameters:

  • app_slug (string, required) - App slug
  • event_name (string, required) - Event type name

Response:

{
"success": true,
"message": "Event 'PRE_USER_CREATE' details retrieved successfully",
"data": {
"event": "PRE_USER_CREATE",
"app": {
"id": 1,
"name": "User Management",
"slug": "user-management"
},
"total_subscribers": 2,
"subscribers": [
{
"subscriber_id": 1,
"event": "PRE_USER_CREATE",
"function_id": 123,
"function_name": "Validate User Data",
"function_slug": "validate-user-data",
"app_id": 1,
"app_name": "User Management",
"app_slug": "user-management",
"created_at": "2025-01-15T10:30:00Z"
},
{
"subscriber_id": 2,
"event": "PRE_USER_CREATE",
"function_id": 124,
"function_name": "Check Email Domain",
"function_slug": "check-email-domain",
"app_id": 1,
"app_name": "User Management",
"app_slug": "user-management",
"created_at": "2025-01-15T11:00:00Z"
}
]
}
}

Example:

curl -X GET https://your-site.taruvi.cloud/api/apps/user-management/events/PRE_USER_CREATE/ \
-H "Authorization: Bearer YOUR_TOKEN"

Response (200 OK):

{
"success": true,
"message": "Event 'PRE_USER_CREATE' details retrieved successfully",
"data": {
"event": "PRE_USER_CREATE",
"total_subscribers": 2,
"subscribers": [...]
}
}

Integration Example: User Creation Flow

The Events system runs around the built-in user creation flow as a concrete example of the PRE / POST pattern.

When a user is created:

  1. PRE_USER_CREATE fires with the proposed user data. All subscribed functions run, and the platform blocks on their results before the user is saved. If any function returns an error, user creation is aborted and the error is returned to the API caller.
  2. The user is persisted only after every PRE_USER_CREATE subscriber succeeds.
  3. POST_USER_CREATE fires with the complete user object (including the new ID). Subscribed functions run in the background — the user creation API call returns immediately and any subscriber errors are logged but do not affect user creation.

The same blocking-vs-fire-and-forget pattern applies to every other PRE / POST pair.

Event Flow Explanation

Step 1: PRE_USER_CREATE Event (Blocking)

  1. Create Event object with validated user data
  2. Call publish_event() which executes all subscribed functions asynchronously
  3. Receive task_ids array with task IDs
  4. Loop through each task ID and block using AsyncResult.get()
  5. If any task fails → abort user creation with ValidationError
  6. If all tasks succeed → continue to save user

Step 2: Save User

Only executed if all PRE events succeed.

Step 3: POST_USER_CREATE Event (Non-blocking)

  1. Create Event object with complete user object (including ID)
  2. Call publish_event() and ignore the response
  3. Functions execute asynchronously in the background
  4. Errors are logged but don't affect user creation

Response Format

The publish_event() method returns:

{
"status": "success",
"message": "Event published successfully",
"data": {
"event": "PRE_USER_CREATE",
"task_ids": [
{
"function_id": 123,
"function_name": "Validate User Data",
"task_id": "task-uuid-1",
"invocation_id": 1
},
{
"function_id": 124,
"function_name": "Check Email Domain",
"task_id": "task-uuid-2",
"invocation_id": 2
}
],
"total_subscribers": 2
}
}

Error case (when function execution fails):

{
"status": "error",
"code": "EXECUTION_ERROR",
"message": "Function execution failed",
"data": {
"function_id": 125,
"function_name": "Failed Function",
"status": "error",
"task_id": null
}
}

Best Practices

When to Use PRE Events

Use PRE events when you need to:

  • Validate data before committing changes
  • Enrich data with information from external systems
  • Check prerequisites (e.g., quota limits, permissions)
  • Block operations that don't meet criteria

Example PRE event functions:

  • Email domain validation
  • Credit check before user activation
  • Data enrichment from CRM
  • Duplicate detection

When to Use POST Events

Use POST events when you need to:

  • Send notifications (email, SMS, webhooks)
  • Log or track events for analytics
  • Sync data to external systems
  • Create side effects that shouldn't block the main operation

Example POST event functions:

  • Send welcome email
  • Create Stripe customer
  • Log to analytics platform
  • Sync to CRM

Error Handling

PRE Events:

  • Always wrap in try/except to handle failures gracefully
  • Provide clear error messages indicating which function failed
  • Consider retry logic for transient failures

POST Events:

  • Log errors but don't re-raise them
  • Consider dead letter queues for failed tasks
  • Monitor execution via invocation records

Performance Considerations

  • Keep functions fast - PRE events block the main operation
  • Use timeouts - Default timeout is 300 seconds (5 minutes)
  • Monitor task queues - Ensure background workers are scaled appropriately
  • Limit subscribers - Too many subscribers can slow down operations

Security

  • Validate event params - Don't trust event data implicitly
  • Use permissions - Control who can subscribe functions to events
  • Sanitize sensitive data - Be careful with passwords, tokens in event params

Internal Use Only

Important: The Events API does not provide a public endpoint to publish events. Events can only be published internally within the application code using the EventService class.

This design ensures:

  • Security - External users cannot trigger arbitrary events
  • Control - Event publishing is tightly controlled by application logic
  • Consistency - Events are only fired at appropriate lifecycle points

To trigger events in your code:

from core.events.services import EventService
from core.events.registry import Event, EventType

# Initialize service
event_service = EventService()

# Create event
event = Event(
event_type=EventType.PRE_USER_CREATE,
params={"username": "john_doe", "email": "john@example.com"}
)

# Publish event
result = event_service.publish_event(event, user=request.user)