Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,23 @@ project adheres to [Semantic Versioning](https://semver.org/).

## [Unreleased]

## [2.6.0] - 2026-09-18

### Added
- `JobKind` gains `photo_mockup_create` and `photo_mockup_render`, the names a
photo-mockup job carries when submitted on `/api/v1/photo-mockups`. A job
submitted through `client.ai` keeps `2d_create` / `2d_render`; both spellings
are typed, and `jobs.list({ kind })` with either returns both.
`Create2DMockupResult.kind` admits both spellings.
- `WebhookEvent` gains `photo_mockup.ready`, `photo_mockup.rejected`,
`photo_mockup.failed`, `photo_mockup_render.succeeded` and
`photo_mockup_render.failed`. An endpoint receives the spelling it is pinned
to; an endpoint created before this release keeps the `2d_mockup.*` /
`2d_render.*` names.
- `WebhookEndpoint.eventNaming` (`'legacy' | 'current'`) reports that pin, and
`webhooks.create` / `webhooks.update` accept `eventNaming` to choose or
change it. Omitted on create, the API pins a new endpoint to `'current'`.

## [2.5.1] - 2026-09-18

### Added
Expand Down
30 changes: 27 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,11 @@ console.log(done.resultUrl)
It throws `TimeoutError` only when the job is still running past `timeoutMs`.

List and page your jobs (keyset pagination, newest first). Filter by `kind`
(`render` | `video` | `upload` | `2d_create`) and/or `mockupUuid`:
(`render` | `video` | `upload` | `2d_create` | `2d_render` |
`photo_mockup_create` | `photo_mockup_render`) and/or `mockupUuid`. A
photo-mockup job is spelled `photo_mockup_*` when submitted on
`/api/v1/photo-mockups` and `2d_*` when submitted through `client.ai`; filtering
by either spelling returns both:

```typescript
const { jobs, nextCursor } = await client.jobs.list({ kind: 'video', limit: 50 })
Expand Down Expand Up @@ -246,8 +250,9 @@ console.log(result.printFiles[0].exportFormat) // 'webp'

Prefer to render in the background? Pass `isAsync: true` and `render()` resolves
with a `Job` of kind `'2d_render'` (`202 Accepted`) you await with
`jobs.waitForJob`. A `2d_render.succeeded` / `2d_render.failed` webhook also
fires.
`jobs.waitForJob`. A `photo_mockup_render.succeeded` /
`photo_mockup_render.failed` webhook also fires (`2d_render.succeeded` /
`2d_render.failed` on an endpoint that keeps the legacy event names).

```typescript
const job = await client.ai.render({
Expand Down Expand Up @@ -541,6 +546,25 @@ await client.webhooks.replayFailed(endpoint.id) // bulk replay all
const events = await client.webhooks.listEvents({ status: 'failed', limit: 100 })
```

Photo-mockup events are `photo_mockup.ready` / `photo_mockup.rejected` /
`photo_mockup.failed` and `photo_mockup_render.succeeded` /
`photo_mockup_render.failed`. Each endpoint is pinned to one spelling
(`eventNaming`): a new endpoint receives these names; an endpoint created
before they existed keeps receiving `2d_mockup.*` / `2d_render.*` until you
re-pin it. Subscribe with either spelling and the delivery carries the
endpoint's own.

```typescript
// A handler that still expects the legacy names:
await client.webhooks.create({
url: 'https://example.com/hooks/sudomock',
eventTypes: ['2d_render.succeeded'],
eventNaming: 'legacy',
})
// Move an older endpoint to the current names once its handler is ready:
await client.webhooks.update(endpoint.id, { eventNaming: 'current' })
```

#### Verifying signatures

Every delivery carries TWO headers -- `X-SudoMock-Signature` (hex HMAC-SHA256
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "sudomock",
"version": "2.5.1",
"version": "2.6.0",
"description": "Official Node.js/TypeScript SDK for the SudoMock API - mockup generation from PSD files",
"type": "module",
"main": "./dist/index.cjs",
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ export type {
CreateVideoParams,
VideoWebhookOverride,
WebhookEvent,
WebhookEventNaming,
WebhookEndpoint,
CreateWebhookEndpointParams,
UpdateWebhookEndpointParams,
Expand Down
7 changes: 5 additions & 2 deletions src/resources/webhooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ function toWebhookEndpoint(value: WebhookEndpoint): WebhookEndpoint {
secret: value.secret,
description: value.description,
eventTypes: value.eventTypes,
eventNaming: value.eventNaming,
enabled: value.enabled,
createdAt: value.createdAt,
updatedAt: value.updatedAt,
Expand Down Expand Up @@ -108,14 +109,15 @@ export class WebhooksResource {
url: params.url,
eventTypes: params.eventTypes ?? [],
description: params.description,
eventNaming: params.eventNaming,
},
})
return toWebhookEndpoint(endpoint)
}

/**
* Update a webhook endpoint (URL, subscribed event types, description, or
* enabled state).
* Update a webhook endpoint (URL, subscribed event types, description,
* enabled state, or the event-name spelling it is pinned to).
*/
async update(
id: string,
Expand All @@ -129,6 +131,7 @@ export class WebhooksResource {
eventTypes: params.eventTypes,
description: params.description,
enabled: params.enabled,
eventNaming: params.eventNaming,
},
})
return toWebhookEndpoint(endpoint)
Expand Down
68 changes: 62 additions & 6 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -521,8 +521,9 @@ export interface AIRenderParams {
* When `true`, the API enqueues the render and immediately returns a
* {@link Job} of kind `'2d_render'` (HTTP 202) instead of blocking until the
* render completes. Poll the job with `client.jobs.retrieve(job.jobId)` or
* `client.jobs.waitForJob(job.jobId)`; a `2d_render.succeeded` /
* `2d_render.failed` webhook also fires.
* `client.jobs.waitForJob(job.jobId)`; a `photo_mockup_render.succeeded` /
* `photo_mockup_render.failed` webhook also fires (`2d_render.succeeded` /
* `2d_render.failed` on an endpoint that keeps the legacy event names).
*
* Default: `false` (synchronous, returns an {@link AIRenderResult}).
*/
Expand Down Expand Up @@ -578,7 +579,8 @@ export type Create2DMockupParams = {
/** Accepted 2D-mockup creation job. */
export interface Create2DMockupResult {
jobId: string
kind: '2d_create'
/** `'2d_create'` from `client.ai`; `'photo_mockup_create'` when submitted on `/api/v1/photo-mockups`. */
kind: '2d_create' | 'photo_mockup_create'
status: 'queued'
statusUrl: string
}
Expand Down Expand Up @@ -760,8 +762,23 @@ export interface UploadResult {
// Jobs (async renders / videos / uploads / 2D creation)
// ---------------------------------------------------------------------------

/** The kind of work a job performs. */
export type JobKind = 'render' | 'video' | 'upload' | '2d_create' | '2d_render'
/**
* The kind of work a job performs.
*
* A photo-mockup job has two spellings: `'photo_mockup_create'` /
* `'photo_mockup_render'` when submitted on `/api/v1/photo-mockups`, and
* `'2d_create'` / `'2d_render'` when submitted on `/api/v1/sudoai/2d-mockups`
* (the path `client.ai` posts to). Filtering {@link JobsResource.list} by
* either spelling returns both.
*/
export type JobKind =
| 'render'
| 'video'
| 'upload'
| '2d_create'
| '2d_render'
| 'photo_mockup_create'
| 'photo_mockup_render'

/**
* Terminal and in-flight statuses for an async job (the API field is `status`).
Expand Down Expand Up @@ -946,7 +963,15 @@ export interface VideoWebhookOverride {
// Webhook endpoints
// ---------------------------------------------------------------------------

/** Event types a webhook endpoint can subscribe to. */
/**
* Event types a webhook endpoint can subscribe to.
*
* Photo-mockup events have two spellings, and an endpoint receives the one it
* is pinned to ({@link WebhookEndpoint.eventNaming}): `photo_mockup.*` /
* `photo_mockup_render.*` on a `'current'` endpoint, `2d_mockup.*` /
* `2d_render.*` on a `'legacy'` one. Subscribe with either spelling; the
* delivery carries the endpoint's own.
*/
export type WebhookEvent =
| 'render.succeeded'
| 'render.failed'
Expand All @@ -958,9 +983,27 @@ export type WebhookEvent =
| '2d_mockup.failed'
| '2d_render.succeeded'
| '2d_render.failed'
| 'photo_mockup.ready'
| 'photo_mockup.rejected'
| 'photo_mockup.failed'
| 'photo_mockup_render.succeeded'
| 'photo_mockup_render.failed'
| 'webhook.test'
| (string & {})

/**
* Which spelling of the photo-mockup events an endpoint receives.
*
* - `'current'` -- `photo_mockup.*` / `photo_mockup_render.*`; the payload's
* `kind` is `photo_mockup_create` / `photo_mockup_render`.
* - `'legacy'` -- `2d_mockup.*` / `2d_render.*`; the payload's `kind` is
* `2d_create` / `2d_render`.
*
* A new endpoint is pinned to `'current'` unless created otherwise; an endpoint
* that predates the current names stays on `'legacy'` until re-pinned.
*/
export type WebhookEventNaming = 'legacy' | 'current'

export interface WebhookEndpoint {
/** Endpoint identifier (API field: `id`). */
id: string
Expand All @@ -975,6 +1018,11 @@ export interface WebhookEndpoint {
description?: string | null
/** Subscribed event types (empty array = subscribe to all events). */
eventTypes: WebhookEvent[]
/**
* The event-name spelling this endpoint is pinned to (API field:
* `event_naming`). Absent on a deployment that predates the field.
*/
eventNaming?: WebhookEventNaming
/** Whether the endpoint is currently active. */
enabled: boolean
createdAt?: string
Expand All @@ -991,13 +1039,21 @@ export interface CreateWebhookEndpointParams {
eventTypes?: WebhookEvent[]
/** Optional description. */
description?: string
/**
* Which spelling of the photo-mockup events this endpoint receives. Omit to
* take the API default (`'current'`); pass `'legacy'` for a handler that
* still expects `2d_mockup.*` / `2d_render.*`.
*/
eventNaming?: WebhookEventNaming
}

export interface UpdateWebhookEndpointParams {
url?: string
eventTypes?: WebhookEvent[]
description?: string
enabled?: boolean
/** Re-pin the endpoint once its handler is ready for the other spelling. */
eventNaming?: WebhookEventNaming
}

/** A single delivery-attempt log row. */
Expand Down
Loading
Loading