Skip to content

Asynchronous Article Updates

The asynchronous article update API allows you to queue screenshot replacements for one or more help center articles in a single request. Jobs are processed in the background via an SQS-backed queue, and you can poll for completion at any time.

This is the recommended way to sync screenshots with your help center. It replaces the legacy synchronous PUT /helpcenter/article/:id approach, which could not safely handle multiple image updates to the same article in quick succession.

Why asynchronous?

When a user clicks "Accept and Sync" on several rows in rapid succession, each update touches the same article on the help center. Synchronous processing caused race conditions - the second update might overwrite the first, or the help center API might reject concurrent writes. The async queue serializes updates per article, automatically delaying overlapping jobs and retrying them after a short cooldown.

For GitHub/Mintlify integrations, asynchronous processing is required: every commit on a Mintlify-tracked branch triggers a full redeploy, so updates are coalesced into a single pull request instead of individual commits.

Supported platforms

PlatformIntegration platformProvider value
Intercomhelpcenterintercom
Zendeskhelpcenterzendesk
Helpjuicehelpcenterhelpjuice
Help Scouthelpcenterhelpscout
Mintlify (via GitHub)githubmintlify

Create article update jobs

POST /helpcenter/article/async

Submits a batch of screenshot replacement jobs. Each job targets a specific article and screenshot. The API validates the request, persists each job to DynamoDB, dispatches messages to the processing queue, and returns immediately with a 202 Accepted.

Request

Headers
HeaderRequiredDescription
x-api-keyYesYour LaunchBrightly API key
Content-TypeYesapplication/json
Body
json
{
  "integrationId": "d9e45598-d24d-40d8-baf6-33b6185c61b3",
  "data": [
    {
      "articleId": "8444283",
      "screenshots": [
        {
          "name": "settings-page.png",
          "metadata": {
            "ImageUniqueID": "601eb123-a472-4ffd-ae8c-0f939ae9df3ex"
          },
          "hash": "4d441888f21eceed2443eef4ba9b4e4d6c198f86904c258d476fed10a6b2e555",
          "url": "https://your-bucket.s3.us-east-1.amazonaws.com/your-team/2026-01-15/screenshot-001.png"
        }
      ]
    }
  ]
}
cURL
bash
curl --location --request POST 'https://api.launchbrightly.com/helpcenter/article/async' \
--header 'x-api-key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
  "integrationId": "d9e45598-d24d-40d8-baf6-33b6185c61b3",
  "data": [
    {
      "articleId": "8444283",
      "screenshots": [
        {
          "name": "settings-page.png",
          "metadata": {
            "ImageUniqueID": "601eb123-a472-4ffd-ae8c-0f939ae9df3ex"
          },
          "hash": "4d441888f21eceed2443eef4ba9b4e4d6c198f86904c258d476fed10a6b2e555",
          "url": "https://your-bucket.s3.us-east-1.amazonaws.com/your-team/2026-01-15/screenshot-001.png"
        }
      ]
    }
  ]
}'
Field reference
FieldTypeRequiredConstraintsDescription
integrationIdstringYesMust be a valid, active integration IDThe integration to use for authentication with the help center. Created via POST /integration.
automergePRbooleanNoDefaults to falseGitHub/Mintlify only. When true, the pull request is automatically merged after creation.
dataarrayYes1–100 itemsArray of article update jobs.
data[].articleIdstringYes-The external article identifier on the help center platform (e.g., Intercom article ID, Zendesk article ID, or the file path for Mintlify).
data[].screenshotsarrayYes1–50 items per articleScreenshots to replace in this article.
data[].screenshots[].metadataobjectYes1–5 key-value pairs; values must be non-empty stringsPNG metadata key-value pairs used for matching. This is the highest-priority matching method.
data[].screenshots[].hashstringYesNon-empty stringThe pixel hash of the screenshot. Used as the second-priority matching method.
data[].screenshots[].namestringNoNon-empty when providedThe filename of the screenshot.
data[].screenshots[].urlstringYesValid HTTP/HTTPS URL; max 4,096 characters; filename portion max 200 charactersA pre-signed or publicly accessible URL to the replacement image.

Success Response

Code: 202 Accepted
json
{
  "articleRequestId": "c3a1f9e2-7b4d-4e8a-9f5c-2d6b8a3e1f7d",
  "jobs": [
    {
      "id": "a1b2c3d4-5e6f-7890-abcd-ef1234567890",
      "status": "pending",
      "createdAt": "2026-02-11T11:01:50.068Z"
    }
  ]
}
FieldDescription
articleRequestIdA unique identifier for this batch. Use it to poll for job status via the status endpoint.
jobsArray of created jobs, one per screenshot in the request.
jobs[].idUnique job identifier.
jobs[].statusInitial status. Will be pending for valid jobs or failed if pre-validation failed (e.g., filename too long).
jobs[].createdAtISO 8601 timestamp of job creation.

INFO

A 202 response means the jobs have been accepted and queued. It does not mean they have completed. Poll the status endpoint to track progress.

Batch request (multiple articles and screenshots)

You can update multiple screenshots across multiple articles in a single request:

cURL
bash
curl --location --request POST 'https://api.launchbrightly.com/helpcenter/article/async' \
--header 'x-api-key: YOUR_API_KEY' \
--header 'Content-Type: application/json' \
--data '{
  "integrationId": "d9e45598-d24d-40d8-baf6-33b6185c61b3",
  "data": [
    {
      "articleId": "8444283",
      "screenshots": [
        {
          "metadata": { "ImageUniqueID": "601eb123-a472-4ffd-ae8c-0f939ae9df3ex" },
          "hash": "4d441888f21eceed2443eef4ba9b4e4d6c198f86904c258d476fed10a6b2e555",
          "url": "https://your-bucket.s3.us-east-1.amazonaws.com/your-team/2026-01-15/screenshot-001.png"
        }
      ]
    },
    {
      "articleId": "8444284",
      "screenshots": [
        {
          "metadata": { "ImageUniqueID": "401qb163-d472-5eed-ae2c-0f931ae2d5fe2" },
          "hash": "b6975b10b36df3cc8e41b16a9be8c4f691aa1a2059d3dbcfde31d16fe6d5f65d",
          "url": "https://your-bucket.s3.us-east-1.amazonaws.com/your-team/2026-01-15/screenshot-002.png"
        }
      ]
    }
  ]
}'
Response
json
{
  "articleRequestId": "c3a1f9e2-7b4d-4e8a-9f5c-2d6b8a3e1f7d",
  "jobs": [
    {
      "id": "a1b2c3d4-5e6f-7890-abcd-ef1234567890",
      "status": "pending",
      "createdAt": "2026-02-11T11:01:50.068Z"
    },
    {
      "id": "f9e8d7c6-5b4a-3210-fedc-ba9876543210",
      "status": "pending",
      "createdAt": "2026-02-11T11:01:50.068Z"
    }
  ]
}

Error Responses

Validation errors - Code: 422

When a required field is missing or a constraint is violated, the API returns a 422 with an errors array. For example, if integrationId is missing:

json
{
  "errors": [
    "integrationId is required."
  ]
}

All validation errors follow this same shape. The table below lists the validation rules that can trigger a 422:

Validation RuleError Message
Body is not an objectThe request body must be an object.
Missing integrationIdintegrationId is required.
automergePR is not a booleanautomergePR must be a boolean.
data is empty or exceeds 100 itemsdata must be an array of article job objects (1 to 100 jobs).
Missing articleId in a jobarticleId is required for each job.
Screenshots array empty or exceeds 50You can only update up to 50 screenshots per (article) job.
Screenshot missing required fieldsThe screenshot must contain metadata, hash, and url fields.
Metadata exceeds 5 key-value pairsmetadata should contain at most 5 key-value pairs
Screenshot URL is not a valid URLscreenshots.*.url is not a valid URL.
Screenshot URL exceeds 4,096 charactersscreenshots.*.url should not be longer than 4096 characters.
Integration is not a help center or GitHub typeThe integrationId provided is not a HelpCenter integration.
Integration credentials are invalidThe API credentials are invalid.

Missing API key - Code: 403
json
{
  "message": "Missing or invalid authentication token.",
  "statusCode": 403,
  "error": {
    "message": "Missing or invalid authentication token.",
    "type": "MISSING_AUTHENTICATION_TOKEN"
  }
}

Check job status

GET /helpcenter/article/request/:articleRequestId

Retrieves the current status and a summary of all jobs associated with an articleRequestId. Use this to poll for completion after submitting jobs.

Request

Headers
HeaderRequiredDescription
x-api-keyYesYour LaunchBrightly API key
URL Parameters
ParameterRequiredDescription
articleRequestIdYesThe batch identifier returned by POST /helpcenter/article/async
cURL
bash
curl --location --request GET 'https://api.launchbrightly.com/helpcenter/article/request/c3a1f9e2-7b4d-4e8a-9f5c-2d6b8a3e1f7d' \
--header 'x-api-key: YOUR_API_KEY'

Success Response - all jobs completed

Code: 200 OK
json
{
  "summary": {
    "total": "2",
    "pending": "0",
    "completed": "2",
    "failed": "0",
    "delayed": "0"
  },
  "jobs": [
    {
      "id": "a1b2c3d4-5e6f-7890-abcd-ef1234567890",
      "externalArticleId": "8444283",
      "name": "settings-page.png",
      "metadata": {
        "ImageUniqueID": "601eb123-a472-4ffd-ae8c-0f939ae9df3ex"
      },
      "hash": "4d441888f21eceed2443eef4ba9b4e4d6c198f86904c258d476fed10a6b2e555",
      "matchType": "metadata",
      "status": "completed",
      "createdAt": "2026-02-11T11:01:50.068Z",
      "updatedAt": "2026-02-11T11:01:52.892Z"
    },
    {
      "id": "f9e8d7c6-5b4a-3210-fedc-ba9876543210",
      "externalArticleId": "8444284",
      "name": "dashboard.png",
      "metadata": {
        "ImageUniqueID": "401qb163-d472-5eed-ae2c-0f931ae2d5fe2"
      },
      "hash": "b6975b10b36df3cc8e41b16a9be8c4f691aa1a2059d3dbcfde31d16fe6d5f65d",
      "matchType": "hash",
      "status": "completed",
      "createdAt": "2026-02-11T11:01:50.068Z",
      "updatedAt": "2026-02-11T11:03:57.712Z"
    }
  ]
}
Response field reference
FieldDescription
summary.totalTotal number of jobs in this batch.
summary.pendingJobs not yet picked up by a worker.
summary.completedJobs that matched and replaced a screenshot successfully.
summary.failedJobs that could not complete (no match found, invalid credentials, max retries exceeded, etc.).
summary.delayedJobs temporarily held back because another update for the same article is in progress. These auto-retry after ~30 seconds.
jobs[].matchTypeWhich matching algorithm found the image: metadata, hash or empty string if no match.
jobs[].statusOne of: pending, processing, completed, failed, delayed.
jobs[].errorPresent only when status is failed. Contains message and type.

Success Response - with a failed job

When a screenshot cannot be matched in the live article, the job's status is failed and an error object is included:

cURL
bash
curl --location --request GET 'https://api.launchbrightly.com/helpcenter/article/request/c3a1f9e2-7b4d-4e8a-9f5c-2d6b8a3e1f7d' \
--header 'x-api-key: YOUR_API_KEY'
Code: 200 OK
json
{
  "summary": {
    "total": "1",
    "pending": "0",
    "completed": "0",
    "failed": "1",
    "delayed": "0"
  },
  "jobs": [
    {
      "id": "a1b2c3d4-5e6f-7890-abcd-ef1234567890",
      "externalArticleId": "8444283",
      "name": "nonexistent-image.png",
      "metadata": {
        "ImageUniqueID": "601eb123-a472-4ffd-ae8c-0f939ae9df3ex"
      },
      "hash": "4d441888f21eceed2443eef4ba9b4e4d6c198f86904c258d476fed10a6b2e555",
      "matchType": "",
      "status": "failed",
      "error": {
        "message": "Screenshot not found in the live article",
        "type": "RuntimeException"
      },
      "createdAt": "2026-02-11T11:45:52.260Z",
      "updatedAt": "2026-02-11T11:45:57.022Z"
    }
  ]
}

Success Response - with a delayed job

When two jobs target the same article in quick succession, the second job is delayed while the first completes:

Code: 200 OK
json
{
  "summary": {
    "total": "2",
    "pending": "0",
    "completed": "1",
    "failed": "0",
    "delayed": "1"
  },
  "jobs": [
    {
      "id": "a1b2c3d4-5e6f-7890-abcd-ef1234567890",
      "externalArticleId": "8444283",
      "name": "settings-page.png",
      "metadata": { "ImageUniqueID": "601eb123-a472-4ffd-ae8c-0f939ae9df3ex" },
      "hash": "4d441888f21eceed2443eef4ba9b4e4d6c198f86904c258d476fed10a6b2e555",
      "matchType": "hash",
      "status": "completed",
      "createdAt": "2026-02-11T11:01:50.068Z",
      "updatedAt": "2026-02-11T11:01:52.892Z"
    },
    {
      "id": "f9e8d7c6-5b4a-3210-fedc-ba9876543210",
      "externalArticleId": "8444283",
      "name": "billing-page.png",
      "metadata": { "ImageUniqueID": "601eb123-a472-4ffd-ae8c-0f939ae9df3ex" },
      "hash": "b6975b10b36df3cc8e41b16a9be8c4f691aa1a2059d3dbcfde31d16fe6d5f65d",
      "matchType": "",
      "status": "delayed",
      "createdAt": "2026-02-11T11:01:50.068Z",
      "updatedAt": "2026-02-11T11:01:54.680Z"
    }
  ]
}

INFO

Delayed jobs automatically retry after ~30 seconds (with ±2 seconds of jitter). A job is retried up to 3 times before being marked as failed with a MaxDelayRetryReached error.

Success Response - GitHub/Mintlify batch

When the batch targets a GitHub/Mintlify integration, the response includes additional top-level fields for the pull request:

cURL
bash
curl --location --request GET 'https://api.launchbrightly.com/helpcenter/article/request/c3a1f9e2-7b4d-4e8a-9f5c-2d6b8a3e1f7d' \
--header 'x-api-key: YOUR_API_KEY'
Code: 200 OK
json
{
  "summary": {
    "total": "1",
    "pending": "0",
    "completed": "1",
    "failed": "0",
    "delayed": "0"
  },
  "pullRequestUrl": "https://github.com/your-org/your-docs-repo/pull/42",
  "pullRequestMerged": true,
  "branch": "lb-c3a1f9e2-7b4d-4e8a-9f5c-2d6b8a3e1f7d",
  "jobs": [
    {
      "id": "a1b2c3d4-5e6f-7890-abcd-ef1234567890",
      "externalArticleId": "docs/getting-started.mdx",
      "name": "settings-page.png",
      "metadata": { "ImageUniqueID": "601eb123-a472-4ffd-ae8c-0f939ae9df3ex" },
      "hash": "4d441888f21eceed2443eef4ba9b4e4d6c198f86904c258d476fed10a6b2e555",
      "matchType": "metadata",
      "status": "completed",
      "createdAt": "2026-02-11T11:01:50.068Z",
      "updatedAt": "2026-02-11T11:01:55.000Z"
    }
  ]
}
FieldDescription
pullRequestUrlURL of the pull request created on the docs repository. null if no changes were needed.
pullRequestMergedtrue if automergePR was set and the PR was merged successfully.
branchThe working branch name (format: lb-<articleRequestId>).

Error Response

Condition: The articleRequestId does not exist - Code: 404
cURL
bash
curl --location --request GET 'https://api.launchbrightly.com/helpcenter/article/request/c3a1f9e2-7b4d-4e8a-9f5c-000000000000' \
--header 'x-api-key: YOUR_API_KEY'
json
{
  "message": "The record c3a1f9e2-7b4d-4e8a-9f5c-000000000000 does not exist or is invalid.",
  "statusCode": 404,
  "error": {
    "message": "The article update job id could not be found: c3a1f9e2-7b4d-4e8a-9f5c-000000000000",
    "type": "RESOURCE_NOT_FOUND"
  }
}

Job lifecycle

A job moves through the following statuses:

pending → processing → completed
                     → failed
        → delayed → processing → completed
                               → failed
                   → delayed → delayed → failed (MaxDelayRetryReached)
StatusDescription
pendingJob created and queued for processing.
processingA worker has picked up the job.
delayedAnother update for the same article is in flight. The job will auto-retry after ~30 seconds.
completedThe screenshot was matched and replaced in the live article.
failedThe job could not complete. Check the error field for details.

Common failure reasons

Error TypeDescription
RuntimeExceptionScreenshot not found in the live article - no match on metadata, hash, or name.
MaxDelayRetryReachedThe job was delayed 3 times (another update kept running).
FILE_NAME_TOO_LONGThe screenshot URL's filename exceeds 200 characters. Pre-validated at submission; the job is created with failed status immediately.

Duplicate detection

To prevent race conditions when multiple updates target the same article, the async worker serializes updates:

  1. Before processing, the worker checks for other recent in-flight jobs (pending, processing, or delayed) targeting the same article and integration, within a 60-second window.
  2. If an earlier job exists, the current job is re-queued with a ~30 second delay (30s base ± 2s jitter).
  3. The job's delayCount is incremented on each delay. After 3 delays, the job fails.
  4. Completed and failed jobs no longer hold a lock - they don't cause subsequent jobs to delay.

This applies only to non-GitHub integrations. GitHub/Mintlify batches are inherently serialized because the entire batch is processed as a single SQS message.


Batch size limits

ConstraintLimit
Jobs per request (data array length)1–100
Screenshots per article job1–50
Metadata key-value pairs per screenshot1–5
Screenshot URL lengthMax 4,096 characters
Screenshot filename length (from URL)Max 200 characters

HTTP status codes

CodeDescription
202Jobs accepted and queued for processing (POST)
200Job status retrieved successfully (GET)
403Unauthorized or missing API key
404articleRequestId not found
422Validation error in request body
500Unexpected server error