How Callbacks Work
The USPTO Filing Bot sends webhook notifications (HTTP POST) to your registered callback URLs whenever an applicant's status changes or a filing progresses. This allows your system to react in real-time without polling.
Key Concepts
- CompanyCallbackUrl — each company (tenant) registers one or more callback URLs. Every event is fan-out delivered to all active URLs for that company.
- Best-effort delivery — callback failures never block the bot. If a delivery fails, it is retried with linear backoff.
- Outbox pattern — events are first written to a
CallbackDeliverytable, then dispatched asynchronously by a background interval (every 10 seconds). - Idempotency — each payload includes a
correlationId(UUID). Use it to de-duplicate on your side.
Delivery Flow
Lifecycle & Callback Flow
Every callback maps to a specific moment in the applicant's lifecycle. This diagram shows when each event fires:
ACCOUNT_CREATED fires both APPLICANT_STATUS_CHANGED and the specific milestone event. Your system should handle both — they share the same correlationId only when emitted from the same action, but are separate delivery rows.
Payload Structure
All callbacks share a stable payload envelope. Fields are only added, never removed (backward-compatible).
Envelope
{
"event": "APPLICANT_STATUS_CHANGED",
"eventId": 2,
"occurredAt": "2026-06-04T15:30:00.000Z",
"correlationId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"applicant": { ... },
"application": { ... }, // only for filing-scoped events
"transition": { ... }, // only when a status transition occurred
"video": { ... }, // only when a bot recording exists
"error": { ... } // only on failure events
}
Envelope Fields
| Field | Type | Always Present | Description |
|---|---|---|---|
event | string | Yes | Event code string (e.g. "APPLICANT_STATUS_CHANGED"). |
eventId | integer | Yes | Numeric event ID from CallbackEvent table. |
occurredAt | ISO 8601 | Yes | UTC timestamp when the event was generated. |
correlationId | UUID | Yes | Unique per-event. Use for idempotency / dedup. |
applicant | object | Yes | Applicant snapshot. See below. |
application | object | No | Present only for filing-scoped events (3, 5, 6). |
transition | object | No | Present when a status transition triggered the event. |
video | object | No | Present when a bot video recording exists for this phase. |
error | object | No | Present on failure events (ACCOUNT_FAILED, SUBMISSION_FAILED, etc.). |
applicant Object
Included in every callback. Represents the applicant's state at the moment the event fired.
| Field | Type | Description |
|---|---|---|
id | integer | Applicant's database ID. |
customerId | integer | Your external customer reference ID. |
companyId | integer | Tenant company ID. |
firstName | string | Applicant's first name. |
lastName | string | Applicant's last name. |
emailAddress | string | Your contact email (NOT the USPTO account email). |
usptoAccountEmail | string | null | Auto-generated USPTO mailbox. null before provisioning. |
usptoAccountPassword | string | null | USPTO account password. Generated at applicant creation. |
mfaSecret | string | null | TOTP MFA secret for USPTO login. null until Phase 1 MFA enrollment completes. |
applicantLifeCycleStatusId | integer | Current lifecycle status ID. |
applicantLifeCycleStatus | string | Human-readable lifecycle status title. |
usptoAccountEmailStatusId | integer | Email provisioning status (1=Pending, 2=Created, 3=Verified, 4=Failed). |
Example applicant Object
{
"id": 42,
"customerId": 1001,
"companyId": 5,
"firstName": "Mike",
"lastName": "Jacob",
"emailAddress": "[email protected]",
"usptoAccountEmail": "[email protected]",
"usptoAccountPassword": "xY9!kP2m@LqR7n",
"mfaSecret": "JBSWY3DPEHPK3PXP",
"applicantLifeCycleStatusId": 9,
"applicantLifeCycleStatus": "Account Created",
"usptoAccountEmailStatusId": 2
}
usptoAccountPassword and mfaSecret are included in every callback payload. Ensure your webhook endpoint uses HTTPS and stores these securely. mfaSecret will be null in callbacks that fire before MFA enrollment (e.g. EMAIL_PROVISIONED, FORM_FILLING).
application Object
Only present for filing-scoped events: FILING_STATUS_CHANGED, SUBMISSION_COMPLETED, SUBMISSION_FAILED.
| Field | Type | Description |
|---|---|---|
id | integer | Application's database ID. |
applicantId | integer | Parent applicant ID. |
filingStatusId | integer | Current filing status ID. |
filingStatus | string | Human-readable filing status title. |
transition Object
Present when an event represents a status change (applicant lifecycle or filing status).
| Field | Type | Description |
|---|---|---|
from | integer | null | Previous status ID. null for the first transition. |
to | integer | New status ID. |
fromTitle | string | null | Human-readable previous status. |
toTitle | string | Human-readable new status. |
video Object
Present when a bot video recording is associated with this event's phase.
| Field | Type | Description |
|---|---|---|
type | string | Recording type: "Signup", "USPTO Verification", or "Filing". |
typeId | integer | Video type ID (1=Signup, 2=Verification, 3=Filing). |
path | string | Server-side file path of the recording. |
durationSeconds | number | Recording duration in seconds. |
recordedAt | ISO 8601 | When the recording was created. |
error Object
Present on failure events.
| Field | Type | Description |
|---|---|---|
message | string | Human-readable error description. |
EMAIL_PROVISIONED
EVENT ID: 10 V1 API
Fired immediately after the third-party email provider successfully creates the USPTO mailbox. This is the first callback you receive for a new applicant.
When it fires
POST /api/v1/applicants/create-applicant→ email provisioning succeeds- Applicant lifecycle transitions from
PENDING (1)toEMAIL_CREATED (2)
Key payload fields at this point
applicant.usptoAccountEmail— the newly created mailbox addressapplicant.usptoAccountPassword— the generated passwordapplicant.mfaSecret—null(MFA not enrolled yet)applicant.applicantLifeCycleStatusId—2(EMAIL_CREATED)
Example payload
{
"event": "EMAIL_PROVISIONED",
"eventId": 10,
"occurredAt": "2026-06-04T15:00:01.000Z",
"correlationId": "c9d1e2f3-a4b5-6789-cdef-012345678901",
"applicant": {
"id": 42,
"customerId": 1001,
"companyId": 5,
"firstName": "Mike",
"lastName": "Jacob",
"emailAddress": "[email protected]",
"usptoAccountEmail": "[email protected]",
"usptoAccountPassword": "xY9!kP2m@LqR7n",
"mfaSecret": null,
"applicantLifeCycleStatusId": 2,
"applicantLifeCycleStatus": "Email Created",
"usptoAccountEmailStatusId": 2
}
}
APPLICANT_STATUS_CHANGED
EVENT ID: 2 PHASE 1 PHASE 2
The universal lifecycle event. Fired on every applicant lifecycle status transition performed by the bot. This is the most frequent callback — you can build a complete audit trail from this event alone.
When it fires
Every call to the bot's internal setLifecycleStatus() function fires this event. That includes all of these transitions:
| From | To | Phase | Video Attached |
|---|---|---|---|
| PENDING (1) | EMAIL_CREATED (2) | API | No |
| EMAIL_CREATED (2) | FORM_FILLING (3) | Phase 1 | No |
| FORM_FILLING (3) | FORM_SUBMITTED (4) | Phase 1 | No |
| FORM_SUBMITTED (4) | EMAIL_VERIFICATION_PENDING (5) | Phase 1 | No |
| EMAIL_VERIFICATION_PENDING (5) | EMAIL_VERIFIED (6) | Phase 1 | No |
| EMAIL_VERIFIED (6) | PASSWORD_SETUP (7) | Phase 1 | No |
| PASSWORD_SETUP (7) | SECURITY_METHOD_SETUP (8) | Phase 1 | No |
| SECURITY_METHOD_SETUP (8) | ACCOUNT_CREATED (9) | Phase 1 | 🎬 Signup |
| Any | ACCOUNT_FAILED (10) | Phase 1 | 🎬 Signup |
| ACCOUNT_CREATED (9) | IDME_VERIFIED (11) | Phase 2 | No |
| ACCOUNT_CREATED (9) | IDME_VERIF_FAILED (12) | Phase 2 | No |
Example payload
{
"event": "APPLICANT_STATUS_CHANGED",
"eventId": 2,
"occurredAt": "2026-06-04T15:05:30.000Z",
"correlationId": "d1e2f3a4-b5c6-7890-abcd-ef0123456789",
"applicant": {
"id": 42,
"customerId": 1001,
"companyId": 5,
"firstName": "Mike",
"lastName": "Jacob",
"emailAddress": "[email protected]",
"usptoAccountEmail": "[email protected]",
"usptoAccountPassword": "xY9!kP2m@LqR7n",
"mfaSecret": null,
"applicantLifeCycleStatusId": 4,
"applicantLifeCycleStatus": "Form Submitted",
"usptoAccountEmailStatusId": 2
},
"transition": {
"from": 3,
"to": 4,
"fromTitle": "Form Filling",
"toTitle": "Form Submitted"
}
}
ACCOUNT_FAILED
EVENT ID: 7 V1 API PHASE 1
Fired when account creation fails. This is a terminal failure — the applicant cannot proceed further without manual intervention.
When it fires
- Email provisioning failure —
POST /create-applicant→ third-party email provider returns error. Lifecycle remainsPENDING (1). - Bot automation failure — any unrecoverable error during Phase 1 (form fill, captcha timeout, email verification timeout, etc.). Lifecycle transitions to
ACCOUNT_FAILED (10).
ACCOUNT_FAILED fires from both the API layer (email provisioning) and the bot processor (automation failure). Check applicant.applicantLifeCycleStatusId to distinguish: if it's 1 (PENDING), the email never provisioned. If it's 10 (ACCOUNT_FAILED), bot automation failed.
Example payload (email provisioning failure)
{
"event": "ACCOUNT_FAILED",
"eventId": 7,
"occurredAt": "2026-06-04T15:00:02.000Z",
"correlationId": "e2f3a4b5-c6d7-8901-bcde-f01234567890",
"applicant": {
"id": 43,
"customerId": 1002,
"companyId": 5,
"firstName": "Jane",
"lastName": "Smith",
"emailAddress": "[email protected]",
"usptoAccountEmail": null,
"usptoAccountPassword": "aB3!mN8x@QwR5z",
"mfaSecret": null,
"applicantLifeCycleStatusId": 1,
"applicantLifeCycleStatus": "Pending",
"usptoAccountEmailStatusId": 4
},
"error": {
"message": "Email provisioning failed: Mailbox creation limit exceeded"
}
}
Example payload (bot automation failure)
{
"event": "ACCOUNT_FAILED",
"eventId": 7,
"occurredAt": "2026-06-04T15:12:45.000Z",
"correlationId": "f3a4b5c6-d7e8-9012-cdef-012345678901",
"applicant": {
"id": 42,
...
"applicantLifeCycleStatusId": 10,
"applicantLifeCycleStatus": "Account Failed",
"usptoAccountEmailStatusId": 2
},
"error": {
"message": "Captcha solve timeout — extension did not solve within 120 seconds"
},
"video": {
"type": "Signup",
"typeId": 1,
"path": "videos/signup_applicant_42_1717513965000.mp4",
"durationSeconds": 125.4,
"recordedAt": "2026-06-04T15:10:00.000Z"
}
}
IDME_VERIFIED
EVENT ID: 8 PHASE 2
Fired when the bot confirms that the applicant has completed ID.me identity verification on USPTO. This is a critical milestone — the applicant is now eligible for trademark filing.
When it fires
- Bot signs into Trademark Center, navigates to
/tm/verify-identity - "ID verification options" text is NOT present → applicant is verified
- Lifecycle transitions from
ACCOUNT_CREATED (9)toIDME_VERIFIED (11)
Also fires alongside: APPLICANT_STATUS_CHANGED (transition 9 → 11)
Example payload
{
"event": "IDME_VERIFIED",
"eventId": 8,
"occurredAt": "2026-06-05T10:30:00.000Z",
"correlationId": "a4b5c6d7-e8f9-0123-abcd-ef4567890123",
"applicant": {
"id": 42,
"customerId": 1001,
"companyId": 5,
"firstName": "Mike",
"lastName": "Jacob",
"emailAddress": "[email protected]",
"usptoAccountEmail": "[email protected]",
"usptoAccountPassword": "xY9!kP2m@LqR7n",
"mfaSecret": "JBSWY3DPEHPK3PXP",
"applicantLifeCycleStatusId": 11,
"applicantLifeCycleStatus": "IDME Verified",
"usptoAccountEmailStatusId": 2
},
"video": {
"type": "USPTO Verification",
"typeId": 2,
"path": "videos/idme_applicant_42_1717578600000.mp4",
"durationSeconds": 45.2,
"recordedAt": "2026-06-05T10:29:15.000Z"
}
}
IDME_VERIFICATION_FAILED
EVENT ID: 9 PHASE 2
Fired when an applicant exceeds the maximum waiting time for ID.me verification. This is a terminal failure — the applicant will not be re-processed.
When it fires
- Applicant has been in
ACCOUNT_CREATED (9)for longer than the configured wait limit (default: 48 hours, configurable viaIDME_WAIT_LIMIT_MSenvironment variable) - Lifecycle transitions to
IDME_VERIFICATION_FAILED_WAITING_LIMIT_EXCEED (12)
Also fires alongside: APPLICANT_STATUS_CHANGED (transition 9 → 12)
Example payload
{
"event": "IDME_VERIFICATION_FAILED",
"eventId": 9,
"occurredAt": "2026-06-06T15:00:00.000Z",
"correlationId": "b5c6d7e8-f9a0-1234-bcde-f56789012345",
"applicant": {
"id": 42,
...
"applicantLifeCycleStatusId": 12,
"applicantLifeCycleStatus": "Idme Verification Failed Waiting Limit Exceed",
"usptoAccountEmailStatusId": 2
},
"video": {
"type": "USPTO Verification",
"typeId": 2,
"path": "videos/idme_applicant_42_1717686000000.mp4",
"durationSeconds": 30.1,
"recordedAt": "2026-06-06T14:59:30.000Z"
}
}
FILING_STATUS_CHANGED
EVENT ID: 3 PHASE 3
Fired on every filing status transition. Includes the application object and a filing-status transition.
When it fires
| From | To | Meaning |
|---|---|---|
| PENDING (1) | UNDER_PROCESS (2) | Bot picked up the application and started filing. |
| UNDER_PROCESS (2) | DONE (3) | Filing completed successfully (draft saved). |
| UNDER_PROCESS (2) | ERROR (4) | Filing failed at some wizard step. |
Example payload
{
"event": "FILING_STATUS_CHANGED",
"eventId": 3,
"occurredAt": "2026-06-06T16:00:00.000Z",
"correlationId": "c6d7e8f9-a0b1-2345-cdef-678901234567",
"applicant": {
"id": 42,
...
"applicantLifeCycleStatusId": 11,
"applicantLifeCycleStatus": "IDME Verified"
},
"application": {
"id": 101,
"applicantId": 42,
"filingStatusId": 3,
"filingStatus": "Done"
},
"transition": {
"from": 2,
"to": 3,
"fromTitle": "Under Process",
"toTitle": "Done"
},
"video": {
"type": "Filing",
"typeId": 3,
"path": "videos/filing_applicant_42_1717689600000.mp4",
"durationSeconds": 340.5,
"recordedAt": "2026-06-06T15:54:20.000Z"
}
}
SUBMISSION_COMPLETED
EVENT ID: 5 PHASE 3
Fired when a trademark filing is saved as a draft on USPTO successfully. This is the final success event in the entire flow.
When it fires
- Filing wizard completed all pages without error
- Filing status transitions to
DONE (3) - Fires alongside:
FILING_STATUS_CHANGED(transition UNDER_PROCESS → DONE)
Example payload
{
"event": "SUBMISSION_COMPLETED",
"eventId": 5,
"occurredAt": "2026-06-06T16:00:01.000Z",
"correlationId": "d7e8f9a0-b1c2-3456-defg-789012345678",
"applicant": { ... },
"application": {
"id": 101,
"applicantId": 42,
"filingStatusId": 3,
"filingStatus": "Done"
},
"video": {
"type": "Filing",
"typeId": 3,
"path": "videos/filing_applicant_42_1717689600000.mp4",
"durationSeconds": 340.5,
"recordedAt": "2026-06-06T15:54:20.000Z"
}
}
SUBMISSION_FAILED
EVENT ID: 6 PHASE 3
Fired when a trademark filing fails at any point during the wizard automation.
When it fires
- Filing wizard encounters an unrecoverable error (validation, page not found, timeout, etc.)
- Filing status transitions to
ERROR (4) - Fires alongside:
FILING_STATUS_CHANGED(transition UNDER_PROCESS → ERROR)
Example payload
{
"event": "SUBMISSION_FAILED",
"eventId": 6,
"occurredAt": "2026-06-06T16:05:00.000Z",
"correlationId": "e8f9a0b1-c2d3-4567-efgh-890123456789",
"applicant": { ... },
"application": {
"id": 102,
"applicantId": 42,
"filingStatusId": 4,
"filingStatus": "Error"
},
"error": {
"message": "Failed at step 'goods-services': Validation error — class 045 not found in ID Manual"
},
"video": {
"type": "Filing",
"typeId": 3,
"path": "videos/filing_applicant_42_1717689900000.mp4",
"durationSeconds": 180.2,
"recordedAt": "2026-06-06T16:02:00.000Z"
}
}
Complete Event Matrix
This table shows every callback event, when it fires, what triggers it, and what extra payload fields are included.
| Event Code | ID | Source | Trigger | application | transition | video | error |
|---|---|---|---|---|---|---|---|
EMAIL_PROVISIONED |
10 | v1 API | Email mailbox created successfully | — | — | — | — |
APPLICANT_STATUS_CHANGED |
2 | Bot | Any lifecycle status transition | — | ✅ | 🎬 on ACCOUNT_CREATED / ACCOUNT_FAILED only | — |
ACCOUNT_FAILED |
7 | API + Bot | Email provision failed OR bot automation failed | — | — | 🎬 Signup | ✅ |
IDME_VERIFIED |
8 | Bot | ID.me verification confirmed | — | — | 🎬 Verification | — |
IDME_VERIFICATION_FAILED |
9 | Bot | ID.me wait limit exceeded (48h default) | — | — | 🎬 Verification | — |
FILING_STATUS_CHANGED |
3 | Bot | Filing status transition (PENDING→UNDER_PROCESS→DONE/ERROR) | ✅ | ✅ (filing status) | 🎬 Filing | — |
SUBMISSION_COMPLETED |
5 | Bot | Filing saved as draft successfully | ✅ | — | 🎬 Filing | — |
SUBMISSION_FAILED |
6 | Bot | Filing terminated with error | ✅ | — | 🎬 Filing | ✅ |
Lookup Tables
Applicant Lifecycle Statuses
| ID | Title | Phase | Terminal? |
|---|---|---|---|
| 1 | Pending | API | No |
| 2 | Email Created | API | No |
| 3 | Form Filling | Phase 1 | No |
| 4 | Form Submitted | Phase 1 | No |
| 5 | Email Verification Pending | Phase 1 | No |
| 6 | Email Verified | Phase 1 | No |
| 7 | Password Setup | Phase 1 | No |
| 8 | Security Method Setup | Phase 1 | No |
| 9 | Account Created | Phase 1 ✅ | No |
| 10 | Account Failed | Phase 1 ❌ | Yes |
| 11 | IDME Verified | Phase 2 ✅ | No |
| 12 | IDME Verification Failed - Waiting Limit Exceeded | Phase 2 ❌ | Yes |
Filing Statuses
| ID | Title | Terminal? |
|---|---|---|
| 1 | Pending | No |
| 2 | Under Process | No |
| 3 | Done | Yes (success) |
| 4 | Error | Yes (failure) |
Callback Event IDs
| ID | Code | Title |
|---|---|---|
| 1 | ALL | Wildcard — matches all events (for URL subscription filtering) |
| 2 | APPLICANT_STATUS_CHANGED | Generic lifecycle transition |
| 3 | FILING_STATUS_CHANGED | Filing pipeline transition |
| 4 | FILING_STEP_ADVANCED | Filing wizard page advanced (reserved) |
| 5 | SUBMISSION_COMPLETED | Filing draft saved successfully |
| 6 | SUBMISSION_FAILED | Filing terminated with error |
| 7 | ACCOUNT_FAILED | Account creation failed |
| 8 | IDME_VERIFIED | ID.me verification completed |
| 9 | IDME_VERIFICATION_FAILED | ID.me wait limit exceeded |
| 10 | EMAIL_PROVISIONED | USPTO mailbox created successfully |
Email Provisioning Statuses
| ID | Title | Description |
|---|---|---|
| 1 | Pending | Applicant created, mailbox not yet provisioned. |
| 2 | Created | Mailbox provisioned successfully. |
| 3 | Verified | Email verified via USPTO activation link. |
| 4 | Failed | Third-party email provider returned error. |
Bot Video Types
| ID | Title | Phase | Attached to Events |
|---|---|---|---|
| 1 | Signup | Phase 1 | APPLICANT_STATUS_CHANGED (→9 or →10), ACCOUNT_FAILED |
| 2 | USPTO Verification | Phase 2 | IDME_VERIFIED, IDME_VERIFICATION_FAILED |
| 3 | Filing | Phase 3 | FILING_STATUS_CHANGED, SUBMISSION_COMPLETED, SUBMISSION_FAILED |
Delivery & Retry Logic
Delivery Status Lifecycle
Delivery Status Reference
| ID | Status | Terminal? | Description |
|---|---|---|---|
| 1 | PENDING | No | Waiting for first dispatch attempt. |
| 2 | DELIVERED | Yes ✅ | Your server returned 2xx. Done. |
| 3 | FAILED_WILL_RETRY | No | Delivery failed, will retry after backoff. |
| 4 | RETRYING | No | Currently being retried by the dispatcher. |
| 5 | GIVEN_UP | Yes ❌ | Exceeded MaxRetries. Will not be retried. |
Configuration (per CompanyCallbackUrl)
| Setting | Description | Example |
|---|---|---|
Url | Your webhook endpoint. | https://api.yourapp.com/webhooks/uspto |
HttpMethod | HTTP method used for delivery. | POST |
MaxRetries | Maximum retry attempts before giving up. | 5 |
RetryBackoffSeconds | Base backoff in seconds. Multiplied by attempt number. | 60 (1st=60s, 2nd=120s, 3rd=180s...) |
TimeoutSeconds | HTTP request timeout per attempt. | 30 |
Security Considerations
Every callback payload includes usptoAccountPassword and mfaSecret (when available). These are real credentials that grant access to the applicant's USPTO account.
- Always use HTTPS for your callback URLs.
- Store credentials securely — encrypt at rest.
- Restrict access to your webhook endpoint.
- Validate the source — consider IP whitelisting or shared secret headers.
Idempotency
Every payload includes a correlationId (UUID v4). If your server receives the same correlationId twice (e.g. due to retry), treat the second delivery as a no-op. Store processed correlationIds for at least the retry window duration.
Response Contract
- Return 2xx to acknowledge successful receipt. The response body is ignored.
- Return non-2xx (or let the request timeout) to trigger a retry.
- Process the payload asynchronously if your logic is slow — respond 200 immediately, then process in background.