@OnEndpoint

Expose custom HTTP endpoints for webhooks, APIs, and integrations.

Overview

The @OnEndpoint decorator exposes methods as HTTP endpoints on your agent's Cloud Run service. Use it for webhooks, custom APIs, health checks, and integration points.

Basic Usage

import { HyperfoldAgent, OnEndpoint } from '@hyperfold/actions-sdk';
@HyperfoldAgent({ name: 'custom-agent', type: 'custom' })
export class CustomAgent {
  @OnEndpoint('/health')
  async healthCheck() {
    return {
      status: 'healthy',
      timestamp: new Date().toISOString(),
      version: '1.2.0',
    };
  }
  @OnEndpoint('/metrics')
  async getMetrics() {
    const stats = await this.analytics.getCurrentStats();
    return {
      active_sessions: stats.sessions,
      requests_per_minute: stats.rpm,
      avg_response_time: stats.avgLatency,
    };
  }
  @OnEndpoint('/webhook/shopify')
  async handleShopifyWebhook(request: Request) {
    const event = request.body;
    await this.processShopifyEvent(event);
    return { received: true };
  }
}

HTTP Methods

Support different HTTP methods for RESTful APIs:

import { OnEndpoint, HttpMethod } from '@hyperfold/actions-sdk';
@HyperfoldAgent({ name: 'api-agent', type: 'custom' })
export class ApiAgent {
  @OnEndpoint('/products')
  async listProducts() {
    return await this.catalog.list();
  }
  @OnEndpoint('/products', { method: 'POST' })
  async createProduct(request: Request) {
    const data = request.body;
    return await this.catalog.create(data);
  }
  @OnEndpoint('/products/:id', { method: 'PUT' })
  async updateProduct(request: Request) {
    const { id } = request.params;
    const data = request.body;
    return await this.catalog.update(id, data);
  }
  @OnEndpoint('/products/:id', { method: 'DELETE' })
  async deleteProduct(request: Request) {
    const { id } = request.params;
    await this.catalog.delete(id);
    return { deleted: true };
  }
  @OnEndpoint('/orders/:id', { methods: ['GET', 'PATCH'] })
  async handleOrder(request: Request) {
    const { id } = request.params;
    if (request.method === 'GET') {
      return await this.orders.get(id);
    } else {
      return await this.orders.update(id, request.body);
    }
  }
}

Request Handling

Access request data including headers, body, and query parameters:

@HyperfoldAgent({ name: 'webhook-handler', type: 'custom' })
export class WebhookHandler {
  @OnEndpoint('/webhook/stripe')
  async handleStripeWebhook(request: Request) {
    const signature = request.headers['stripe-signature'];
    const isValid = await this.stripe.verifySignature(
      request.rawBody,
      signature
    );
    if (!isValid) {
      return {
        status: 401,
        body: { error: 'Invalid signature' },
      };
    }
    const event = request.body;
    switch (event.type) {
      case 'payment_intent.succeeded':
        await this.handlePaymentSuccess(event.data);
        break;
      case 'payment_intent.failed':
        await this.handlePaymentFailure(event.data);
        break;
    }
    return { received: true };
  }
  @OnEndpoint('/api/search')
  async searchEndpoint(request: Request) {
    const query = request.query.q;
    const limit = parseInt(request.query.limit) || 10;
    const page = parseInt(request.query.page) || 1;
    const results = await this.catalog.search(query, { limit, page });
    return {
      status: 200,
      headers: {
        'X-Total-Count': results.total.toString(),
        'X-Page': page.toString(),
      },
      body: results.items,
    };
  }
  @OnEndpoint('/upload', { method: 'POST' })
  async handleUpload(request: Request) {
    const files = request.files;
    const fields = request.body;
    for (const file of files) {
      await this.storage.upload(file.buffer, file.originalName);
    }
    return { uploaded: files.length };
  }
}

Request Object

PropertyTypeDescription
methodstringHTTP method (GET, POST, etc.)
headersobjectRequest headers
bodyanyParsed request body
rawBodyBufferRaw body for signature verification
queryobjectQuery string parameters
paramsobjectURL path parameters
filesFile[]Uploaded files (multipart)

Authentication

Secure endpoints with various authentication methods:

import { OnEndpoint, Auth } from '@hyperfold/actions-sdk';
@HyperfoldAgent({ name: 'secure-agent', type: 'custom' })
export class SecureAgent {
  @OnEndpoint('/public/health')
  async publicHealth() {
    return { status: 'ok' };
  }
  @OnEndpoint('/api/data', { auth: 'api_key' })
  async protectedData(request: Request) {
    console.log(`Request from: ${request.auth.keyId}`);
    return { data: 'protected' };
  }
  @OnEndpoint('/api/user', { auth: 'jwt' })
  async getUserData(request: Request) {
    const userId = request.auth.sub;
    return await this.users.get(userId);
  }
  @OnEndpoint('/webhook/partner', { auth: 'custom' })
  async partnerWebhook(request: Request) {
    const apiKey = request.headers['x-partner-key'];
    const isValid = await this.validatePartnerKey(apiKey);
    if (!isValid) {
      return { status: 403, body: { error: 'Invalid partner key' } };
    }
    return await this.processPartnerEvent(request.body);
  }
  @OnEndpoint('/admin/stats', { auth: 'jwt', roles: ['admin'] })
  async adminStats(request: Request) {
    return await this.analytics.getAdminStats();
  }
}
// Configure auth in agent decorator
@HyperfoldAgent({
  name: 'secure-agent',
  type: 'custom',
  auth: {
    apiKey: {
      header: 'X-API-Key',
      validate: async (key) => {
        return await db.apiKeys.exists(key);
      },
    },
    jwt: {
      issuer: 'https://auth.example.com',
      audience: 'hyperfold-agent',
    },
  },
})
export class SecureAgentWithConfig { }