Skip to content

GovCloud Batch Jobs REST API

This document covers the Batch Jobs REST API available in GovCloud deployments that include the Headless API deployment package.

The REST API provides programmatic access to document processing via /jobs endpoints on a Private API Gateway accessible only from within your VPC. It requires:

  • OAuth2 bearer token authentication via Cognito client credentials

Generate a bearer token using the provided script:

Terminal window
# Print token to stdout
./scripts/get_api_token.sh <STACK_NAME>
# Copy to clipboard (macOS)
./scripts/get_api_token.sh <STACK_NAME> | pbcopy

The token is a Cognito client credentials grant with idp-api/jobs.read and idp-api/jobs.write scopes.

The API uses per-client job ownership: each job is visible only to the API client (Cognito app client) that created it.

  • On POST /jobs, the caller’s Cognito principal (sub / client_id claim from the bearer token) is persisted on the job record as CreatedBy.
  • On GET /jobs/{job_id}, the caller’s principal is compared to the job’s CreatedBy. Mismatches return HTTP 404 (not 403) to avoid leaking the existence of jobs owned by other clients.

Implications:

  • Single-client deployments — the default from the bundled CloudFormation template, which creates exactly one AWS::Cognito::UserPoolClient (ApiAppClient) — see no behavioral change from this model. The one client is always the creator and always the reader.
  • Multi-client deployments — if you create additional app clients against ApiUserPool (for credential rotation, per-service credentials, per-team isolation, etc.), each client sees only its own jobs. Cross-client job visibility is not supported through this API.
  • Credential rotation — if you rotate an app client’s secret (same client, new secret), the client’s sub stays constant so previously-created jobs remain visible. If you replace the client entirely (delete + recreate), the new client will not be able to read jobs created by the old one.
  • Jobs predating this model — job records without a CreatedBy field (written by builds from before this feature existed) remain readable by any authenticated caller. This is intentional so in-place upgrades don’t orphan in-flight jobs.
  • Operator-level access — if you need cross-client visibility (audit, debugging, bulk export), read from the S3 OutputBucket directly with IAM credentials. The Jobs API is not the right surface for this.

The API_GATEWAY_ENDPOINT is the ApiGatewayEndpoint value from your CloudFormation stack outputs, in the format https://{restapi-id}.execute-api.{region}.amazonaws.com/{stage}.

Note: The {stage} defaults to beta unless overridden via the ApiStageName parameter.

Creates a new processing job and returns a presigned upload URL.

Terminal window
TOKEN=$(./scripts/get_api_token.sh <STACK_NAME>)
curl -X POST ${API_GATEWAY_ENDPOINT}/jobs \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"fileName": "documents.zip"}'

Response:

{
"jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"upload": {
"uploadUrl": "https://input-bucket.s3.amazonaws.com",
"expiresInSeconds": 3600,
"requiredHeaders": {
"key": "jobs/a1b2c3d4-e5f6-7890-abcd-ef1234567890/archive.zip",
"Content-Type": "application/zip",
"x-amz-credential": "...",
"x-amz-date": "...",
"x-amz-security-token": "...",
"x-amz-algorithm": "...",
"policy": "...",
"x-amz-signature": "..."
}
}
}

Use the presigned URL and requiredHeaders from the POST response:

Terminal window
curl -X POST "<uploadUrl from response>" \
-F "Content-Type=<Content-Type from requiredHeaders>" \
-F "key=<key from requiredHeaders>" \
-F "x-amz-algorithm=<x-amz-algorithm from requiredHeaders>" \
-F "x-amz-credential=<x-amz-credential from requiredHeaders>" \
-F "x-amz-date=<x-amz-date from requiredHeaders>" \
-F "x-amz-security-token=<x-amz-security-token from requiredHeaders>" \
-F "policy=<policy from requiredHeaders>" \
-F "x-amz-signature=<x-amz-signature from requiredHeaders>" \
-F "file=@documents.zip"
Terminal window
curl ${API_GATEWAY_ENDPOINT}/jobs/{job_id} \
-H "Authorization: Bearer $TOKEN"

Response (in progress):

{
"jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "IN_PROGRESS",
"timestamps": {
"createdAt": "2026-01-23T10:00:00Z",
"updatedAt": "2026-01-23T10:05:00Z"
},
"files": {
"document_a.pdf": "COMPLETED",
"document_b.pdf": "IN_PROGRESS"
}
}

Response (succeeded):

{
"jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "SUCCEEDED",
"timestamps": {
"createdAt": "2026-01-23T10:00:00Z",
"updatedAt": "2026-01-23T10:10:00Z"
},
"files": {
"document_a.pdf": "COMPLETED",
"document_b.pdf": "COMPLETED"
},
"result": {
"downloadUrl": "https://output-bucket.s3.amazonaws.com/jobs/.../results.zip?...",
"expiresInSeconds": 3600
}
}

Response (partially succeeded):

{
"jobId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "PARTIALLY_SUCCEEDED",
"timestamps": {
"createdAt": "2026-01-23T10:00:00Z",
"updatedAt": "2026-01-23T10:10:00Z"
},
"files": {
"document_a.pdf": "COMPLETED",
"document_b.pdf": "FAILED"
},
"result": {
"downloadUrl": "https://output-bucket.s3.amazonaws.com/jobs/.../results.zip?...",
"expiresInSeconds": 3600
}
}
StatusDescription
PENDING_UPLOADJob created, awaiting ZIP upload
IN_PROGRESSFiles being processed
SUCCEEDEDAll files completed
PARTIALLY_SUCCEEDEDSome files completed, some failed/aborted. The results.zip will not include output data from documents that did not complete processing
ABORTEDAll files aborted
FAILEDAll files failed or in failed/aborted states

If you deployed with the Bastion package, you can access the private API Gateway from your local machine.

  • AWS CLI configured with credentials for the target account
  • AWS Session Manager plugin — required for the SSM-based SSH tunnel
  • SSH client
Terminal window
./scripts/bastion.sh <STACK_NAME>

In a separate terminal:

Terminal window
# Print token to stdout
./scripts/get_api_token.sh <STACK_NAME>
# Copy to clipboard (macOS)
./scripts/get_api_token.sh <STACK_NAME> | pbcopy
Terminal window
# Create a job (get endpoint URL from stack outputs: ApiGatewayEndpoint)
curl -X POST {api-endpoint}/jobs \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"fileName": "my-documents.zip"}'