@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

typescript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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 };
}
}
Endpoints are accessible at https://<agent-url>/<path>. The agent URL is shown after deployment.

HTTP Methods

Support different HTTP methods for RESTful APIs:

typescript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import { OnEndpoint, HttpMethod } from '@hyperfold/actions-sdk';
@HyperfoldAgent({ name: 'api-agent', type: 'custom' })
export class ApiAgent {
// Default is GET
@OnEndpoint('/products')
async listProducts() {
return await this.catalog.list();
}
// Explicit POST
@OnEndpoint('/products', { method: 'POST' })
async createProduct(request: Request) {
const data = request.body;
return await this.catalog.create(data);
}
// PUT for updates
@OnEndpoint('/products/:id', { method: 'PUT' })
async updateProduct(request: Request) {
const { id } = request.params;
const data = request.body;
return await this.catalog.update(id, data);
}
// DELETE
@OnEndpoint('/products/:id', { method: 'DELETE' })
async deleteProduct(request: Request) {
const { id } = request.params;
await this.catalog.delete(id);
return { deleted: true };
}
// Multiple methods
@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:

typescript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
@HyperfoldAgent({ name: 'webhook-handler', type: 'custom' })
export class WebhookHandler {
@OnEndpoint('/webhook/stripe')
async handleStripeWebhook(request: Request) {
// Access headers
const signature = request.headers['stripe-signature'];
// Verify webhook signature
const isValid = await this.stripe.verifySignature(
request.rawBody,
signature
);
if (!isValid) {
return {
status: 401,
body: { error: 'Invalid signature' },
};
}
// Parse body (automatically parsed for JSON)
const event = request.body;
// Handle different event types
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) {
// Access query parameters
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 with custom status and headers
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) {
// Handle multipart form data
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:

typescript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
import { OnEndpoint, Auth } from '@hyperfold/actions-sdk';
@HyperfoldAgent({ name: 'secure-agent', type: 'custom' })
export class SecureAgent {
// Public endpoint (no auth)
@OnEndpoint('/public/health')
async publicHealth() {
return { status: 'ok' };
}
// API key authentication
@OnEndpoint('/api/data', { auth: 'api_key' })
async protectedData(request: Request) {
// API key validated automatically
// Access key info via request.auth
console.log(`Request from: ${request.auth.keyId}`);
return { data: 'protected' };
}
// JWT authentication
@OnEndpoint('/api/user', { auth: 'jwt' })
async getUserData(request: Request) {
// JWT validated and decoded
const userId = request.auth.sub;
return await this.users.get(userId);
}
// Custom authentication
@OnEndpoint('/webhook/partner', { auth: 'custom' })
async partnerWebhook(request: Request) {
// Implement custom auth logic
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);
}
// Role-based access
@OnEndpoint('/admin/stats', { auth: 'jwt', roles: ['admin'] })
async adminStats(request: Request) {
// Only accessible by users with admin role
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) => {
// Custom validation logic
return await db.apiKeys.exists(key);
},
},
jwt: {
issuer: 'https://auth.example.com',
audience: 'hyperfold-agent',
},
},
})
export class SecureAgentWithConfig { }
Always authenticate webhook endpoints to prevent unauthorized access. Use signature verification for payment and order webhooks.
Explore built-in tools in Tools Reference.