feat: inbound webhooks — public ingest endpoints

core.inbound_hooks (migration 00013): POST /api/v1/hooks/<token> accepts
external events. event mode republishes as inbound.received through the
signed dispatcher + realtime; job mode triggers a scheduled-job run with
the received body as payload. Optional X-Primora-Signature HMAC verify.
Dashboard card on the Integrations page.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Tomas DvorakandDevin committed 2026-09-24 19:15:12 +02:00
1 parent 2f4de543a9
commit 45a1621909
26 files changed
+1264 -9

No files matched your search

@@ -37,6 +37,7 @@ export type { CreatedApiKey } from './models/CreatedApiKey';
export { CreateDBConnectionRequest } from './models/CreateDBConnectionRequest';
export type { CreateDeployMarkerRequest } from './models/CreateDeployMarkerRequest';
export type { CreateDocumentRequest } from './models/CreateDocumentRequest';
export { CreateInboundHookRequest } from './models/CreateInboundHookRequest';
export { CreateIntegrationRequest } from './models/CreateIntegrationRequest';
export { CreateInvitationRequest } from './models/CreateInvitationRequest';
export type { CreateOrganizationRequest } from './models/CreateOrganizationRequest';
@@ -69,6 +70,9 @@ export type { DocumentListResponse } from './models/DocumentListResponse';
export type { ErrorEnvelope } from './models/ErrorEnvelope';
export type { EventSeriesBucket } from './models/EventSeriesBucket';
export type { HealthStatus } from './models/HealthStatus';
export { InboundHook } from './models/InboundHook';
export type { InboundHookListResponse } from './models/InboundHookListResponse';
export type { InboundReceiveResponse } from './models/InboundReceiveResponse';
export { IngestEvent } from './models/IngestEvent';
export type { IngestRequest } from './models/IngestRequest';
export type { IngestResponse } from './models/IngestResponse';
@@ -173,6 +177,7 @@ export { $CreatedApiKey } from './schemas/$CreatedApiKey';
export { $CreateDBConnectionRequest } from './schemas/$CreateDBConnectionRequest';
export { $CreateDeployMarkerRequest } from './schemas/$CreateDeployMarkerRequest';
export { $CreateDocumentRequest } from './schemas/$CreateDocumentRequest';
export { $CreateInboundHookRequest } from './schemas/$CreateInboundHookRequest';
export { $CreateIntegrationRequest } from './schemas/$CreateIntegrationRequest';
export { $CreateInvitationRequest } from './schemas/$CreateInvitationRequest';
export { $CreateOrganizationRequest } from './schemas/$CreateOrganizationRequest';
@@ -205,6 +210,9 @@ export { $DocumentListResponse } from './schemas/$DocumentListResponse';
export { $ErrorEnvelope } from './schemas/$ErrorEnvelope';
export { $EventSeriesBucket } from './schemas/$EventSeriesBucket';
export { $HealthStatus } from './schemas/$HealthStatus';
export { $InboundHook } from './schemas/$InboundHook';
export { $InboundHookListResponse } from './schemas/$InboundHookListResponse';
export { $InboundReceiveResponse } from './schemas/$InboundReceiveResponse';
export { $IngestEvent } from './schemas/$IngestEvent';
export { $IngestRequest } from './schemas/$IngestRequest';
export { $IngestResponse } from './schemas/$IngestResponse';
@@ -284,6 +292,7 @@ export { AutomationService } from './services/AutomationService';
export { CollectionsService } from './services/CollectionsService';
export { DatabasesService } from './services/DatabasesService';
export { HealthService } from './services/HealthService';
export { InboundService } from './services/InboundService';
export { InstanceService } from './services/InstanceService';
export { IntegrationsService } from './services/IntegrationsService';
export { OrganizationsService } from './services/OrganizationsService';
@@ -0,0 +1,24 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type CreateInboundHookRequest = {
name: string;
mode?: CreateInboundHookRequest.mode;
/**
* Required when mode=job — scheduled job to trigger.
*/
job_id?: string;
/**
* Optional — stored encrypted; callers must then sign bodies with X-Primora-Signature.
*/
secret?: string;
enabled?: boolean;
};
export namespace CreateInboundHookRequest {
export enum mode {
EVENT = 'event',
JOB = 'job',
}
}
@@ -8,7 +8,7 @@ export type CreateWebhookRequest = {
* HMAC key for X-Primora-Signature. Generated when omitted.
*/
secret?: string;
events?: Array<'issue.created' | 'deploy.marker' | 'webhook.test' | 'job.run' | 'alert.fired' | 'alert.resolved' | 'document.created' | 'document.updated' | 'document.deleted' | 'object.created' | 'object.updated' | 'object.deleted'>;
events?: Array<'issue.created' | 'deploy.marker' | 'webhook.test' | 'job.run' | 'alert.fired' | 'alert.resolved' | 'inbound.received' | 'document.created' | 'document.updated' | 'document.deleted' | 'object.created' | 'object.updated' | 'object.deleted'>;
enabled?: boolean;
};
@@ -0,0 +1,27 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type InboundHook = {
id: string;
project_id: string;
name: string;
token: string;
/**
* Full ingest URL — POST here from the external system.
*/
url: string;
mode: InboundHook.mode;
job_id?: string;
has_secret: boolean;
enabled: boolean;
last_received_at?: string | null;
created_at: string;
};
export namespace InboundHook {
export enum mode {
EVENT = 'event',
JOB = 'job',
}
}
@@ -0,0 +1,9 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { InboundHook } from './InboundHook';
export type InboundHookListResponse = {
items: Array<InboundHook>;
};
@@ -0,0 +1,10 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export type InboundReceiveResponse = {
received: boolean;
event?: string;
run_id?: string;
};
@@ -8,7 +8,7 @@ export type UpdateWebhookRequest = {
* Replace the signing secret. Omit to keep the current one.
*/
secret?: string;
events?: Array<'issue.created' | 'deploy.marker' | 'webhook.test' | 'job.run' | 'alert.fired' | 'alert.resolved' | 'document.created' | 'document.updated' | 'document.deleted' | 'object.created' | 'object.updated' | 'object.deleted'>;
events?: Array<'issue.created' | 'deploy.marker' | 'webhook.test' | 'job.run' | 'alert.fired' | 'alert.resolved' | 'inbound.received' | 'document.created' | 'document.updated' | 'document.deleted' | 'object.created' | 'object.updated' | 'object.deleted'>;
enabled?: boolean;
};
@@ -7,7 +7,7 @@ export type Webhook = {
project_id: string;
url: string;
/**
* Event filter (issue.created, deploy.marker, webhook.test, job.run, alert.fired, alert.resolved, document.*, object.*). Empty means all events.
* Event filter (issue.created, deploy.marker, webhook.test, job.run, alert.fired, alert.resolved, inbound.received, document.*, object.*). Empty means all events.
*/
events: Array<string>;
enabled: boolean;
@@ -0,0 +1,26 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export const $CreateInboundHookRequest = {
properties: {
name: {
type: 'string',
isRequired: true,
},
mode: {
type: 'Enum',
},
job_id: {
type: 'string',
description: `Required when mode=job — scheduled job to trigger.`,
},
secret: {
type: 'string',
description: `Optional — stored encrypted; callers must then sign bodies with X-Primora-Signature.`,
},
enabled: {
type: 'boolean',
},
},
} as const;
@@ -0,0 +1,56 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export const $InboundHook = {
properties: {
id: {
type: 'string',
isRequired: true,
format: 'uuid',
},
project_id: {
type: 'string',
isRequired: true,
format: 'uuid',
},
name: {
type: 'string',
isRequired: true,
},
token: {
type: 'string',
isRequired: true,
},
url: {
type: 'string',
description: `Full ingest URL — POST here from the external system.`,
isRequired: true,
},
mode: {
type: 'Enum',
isRequired: true,
},
job_id: {
type: 'string',
},
has_secret: {
type: 'boolean',
isRequired: true,
},
enabled: {
type: 'boolean',
isRequired: true,
},
last_received_at: {
type: 'string',
isNullable: true,
format: 'date-time',
},
created_at: {
type: 'string',
isRequired: true,
format: 'date-time',
},
},
} as const;
@@ -0,0 +1,15 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export const $InboundHookListResponse = {
properties: {
items: {
type: 'array',
contains: {
type: 'InboundHook',
},
isRequired: true,
},
},
} as const;
@@ -0,0 +1,18 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
export const $InboundReceiveResponse = {
properties: {
received: {
type: 'boolean',
isRequired: true,
},
event: {
type: 'string',
},
run_id: {
type: 'string',
},
},
} as const;
@@ -0,0 +1,116 @@
/* generated using openapi-typescript-codegen -- do not edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { CreateInboundHookRequest } from '../models/CreateInboundHookRequest';
import type { InboundHook } from '../models/InboundHook';
import type { InboundHookListResponse } from '../models/InboundHookListResponse';
import type { InboundReceiveResponse } from '../models/InboundReceiveResponse';
import type { CancelablePromise } from '../core/CancelablePromise';
import { OpenAPI } from '../core/OpenAPI';
import { request as __request } from '../core/request';
export class InboundService {
/**
* Lists inbound webhook endpoints for the project.
* @returns InboundHookListResponse Inbound hooks
* @throws ApiError
*/
public static listInboundHooks({
projectId,
}: {
projectId: string,
}): CancelablePromise<InboundHookListResponse> {
return __request(OpenAPI, {
method: 'GET',
url: '/projects/{projectID}/inbound-hooks',
path: {
'projectID': projectId,
},
errors: {
401: `Error response`,
},
});
}
/**
* Creates an inbound hook. `event` mode republishes bodies as `inbound.received` domain events; `job` mode triggers a scheduled-job run with the body as its payload. Set `secret` to require `X-Primora-Signature` (HMAC-SHA256 over the raw body).
* @returns InboundHook Inbound hook created
* @throws ApiError
*/
public static createInboundHook({
projectId,
requestBody,
}: {
projectId: string,
requestBody: CreateInboundHookRequest,
}): CancelablePromise<InboundHook> {
return __request(OpenAPI, {
method: 'POST',
url: '/projects/{projectID}/inbound-hooks',
path: {
'projectID': projectId,
},
body: requestBody,
mediaType: 'application/json',
errors: {
400: `Error response`,
401: `Error response`,
},
});
}
/**
* @returns void
* @throws ApiError
*/
public static deleteInboundHook({
projectId,
hookId,
}: {
projectId: string,
hookId: string,
}): CancelablePromise<void> {
return __request(OpenAPI, {
method: 'DELETE',
url: '/projects/{projectID}/inbound-hooks/{hookID}',
path: {
'projectID': projectId,
'hookID': hookId,
},
errors: {
401: `Error response`,
404: `Error response`,
},
});
}
/**
* Public ingest endpoint — the URL token is the credential. Body must be JSON (max 256 KiB); sign with X-Primora-Signature when the hook has a secret.
* @returns InboundReceiveResponse Accepted
* @throws ApiError
*/
public static receiveInboundHook({
token,
xPrimoraSignature,
requestBody,
}: {
token: string,
xPrimoraSignature?: string,
requestBody?: Record<string, any>,
}): CancelablePromise<InboundReceiveResponse> {
return __request(OpenAPI, {
method: 'POST',
url: '/hooks/{token}',
path: {
'token': token,
},
headers: {
'X-Primora-Signature': xPrimoraSignature,
},
body: requestBody,
mediaType: 'application/json',
errors: {
400: `Error response`,
401: `Error response`,
404: `Error response`,
},
});
}
}