Check job status by UUID

Retrieves the current status of an asynchronous job by providing the job UUID and payload hash. This endpoint is used to poll for job completion status after submitting a case file creation request. The endpoint is rate-limited to 20 requests per minute per submission. No authentication is required as the UUID and payload hash provide the necessary security.


Open API 3.0 Specs

The Open API 3.0 Specs are available on the following urls:

Job Status Check Documentation

This document describes how to check the status of asynchronous case file creation jobs using the Penneo API.

Table of Contents


Overview

When you create a case file using the Penneo API, the operation is processed asynchronously. The API immediately returns a job UUID and payload hash, which you can use to poll for the job's completion status.

Key Features:

  • No authentication required (UUID + payload hash provide security)
  • Rate limited to 20 requests per minute per submission
  • Real-time job status tracking
  • Detailed result information when completed

Authentication

No authentication required for this endpoint. Security is provided through:

  • The unique job UUID (returned when job is created)
  • The payload hash

These two values together ensure only authorized users can check job status.


Request Structure

Request Body

The request body must be a JSON object with two required fields.

Required Properties

PropertyTypeFormatDescriptionExample
uuidstringUUID v4The unique identifier of the job to check"123e4567-e89b-12d3-a456-426614174000"
payloadHashstringSHA-256 hashThe hash of the original payload used for verification"3a7bd3e2360a3d485f2a5f4d8e6f1e8b9c6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f"

Request Example

{
  "uuid": "123e4567-e89b-12d3-a456-426614174000",
  "payloadHash": "3a7bd3e2360a3d485f2a5f4d8e6f1e8b9c6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f"
}

Getting UUID and Payload Hash

When you create a case file, the API returns these values in the response:

{
  "message": "Job created",
  "status": "handled",
  "jobs": [
    {
      "uuid": "123e4567-e89b-12d3-a456-426614174000",
      "payloadHash": "3a7bd3e2360a3d485f2a5f4d8e6f1e8b9c6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f",
      ...
    }
  ]
}

💡 Tip: Store both the uuid and payloadHash immediately after creating a job so you can check its status later.


Response Structure

Success Response (200 OK)

When the job is found, you receive a complete job object with all details.

Response Properties

PropertyTypeFormatRequiredDescription
uuidstringUUIDYesUnique identifier for the job
jobStatusstringenumYesCurrent status of the job. See Job Status Values
payloadobjectJSONYesThe original payload submitted with the job
payloadHashstringSHA-256YesSHA-256 hash of the payload
resultobject | nullJSONYesThe result of job execution (populated when jobStatus is "completed")
createdAtstringISO 8601YesDate and time when the job was created
updatedAtstringISO 8601YesDate and time when the job was last updated
errorMessagestring | null-YesError message if job failed (only present when jobStatus is "failed")

Automatic Retry of Failed Jobs

📘

In our response, we return the retries and maxRetries properties, which are defaulted to 0. Currently, automatic retry of failed jobs is not enabled as it is part of our experimental features. We will update this information once the mechanism is available and the documentation is complete.

Success Response Example

{
  "uuid": "7e47477c-e543-43ad-90a9-50e6abe9abdc",
  "type": "etl-queue",
  "payloadHash": "1d3644ac95ec37b073d9d8be2dca5da66f36533397a88c0462dcc219d049db70",
  "jobStatus": "completed",
  "errorMessage": null,
  "retries": 0,
  "maxRetries": 0,
  "createdAt": "2025-11-11T10:34:46.140Z",
  "updatedAt": "2025-11-11T10:34:51.574Z",
  "result": {
    "data": {
      "caseFile": {
        "id": 1234
      },
      "signingLinks": [
        {
          "name": "John Doe",
          "role": "signer",
          "signerId": 12345,
          "signOrder": [
            0
          ],
          "signingLink": "https://app.penneo.com/signing/12345-ABCDE-56789-FGHIJ-KLMNO-PQRS"
        }
      ]
    },
    "errors": null,
    "success": true
  }
}

Error Response (404 Not Found)

When the job is not found or the payload hash doesn't match.

Error Properties

PropertyTypeDescriptionExample
statusCodenumberHTTP status code404
messagestringHuman-readable error message"Job not found"

Error Response Example

{
  "statusCode": 404,
  "message": "Job not found"
}

Common reasons for 404:

  • Job UUID doesn't exist
  • Payload hash doesn't match the job
  • Job has expired (retention policy applied)
  • Job has been deleted

Job Status Values

The jobStatus field indicates the current state of the job.

StatusDescriptionNext Action
pendingJob is waiting to be processedContinue polling
processingJob is currently being executedContinue polling
completedJob finished successfullyCheck result field for outcome
failedJob execution failedCheck errorMessage field for details

Status Workflow

┌─────────┐
│ pending │ → Job created, waiting in queue
└────┬────┘
     ↓
┌────────────┐
│ processing │ → Job is being executed
└─────┬──────┘
      ↓
   ┌──┴──────────────────────────┐
   │                             │
   ↓                             ↓
┌──────────┐                  ┌────────┐
│completed │ → get CF defails │ failed │ → Check errorMessage
└──────────┘                  └────────┘

Examples

Checking Job Status (Completed)

Request:

const url = 'https://sandbox.penneo.com/send/api/v1/queue/public/status';

const data = {
  uuid: '123e4567-e89b-12d3-a456-426614174000',
  payloadHash: '3a7bd3e2360a3d485f2a5f4d8e6f1e8b9c6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f'
};

async function checkQueueStatus() {
  try {
    const response = await fetch(url, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data)
    });

    const responseBody = await response.text();

    if (!response.ok) {
      console.error('Error status:', response.status);
      console.error('Error body:', responseBody);
      return;
    }

    // If response is JSON, parse it
    const result = JSON.parse(responseBody);
    console.log('Queue Status:', result);

  } catch (error) {
    console.error('Fetch failed:', error.message);
  }
}

checkQueueStatus();

Response:

{
  "uuid": "123e4567-e89b-12d3-a456-426614174000",
  "type": "etl-queue",
  "payloadHash": "3a7bd3e2360a3d485f2a5f4d8e6f1e8b9c6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f",
  "jobStatus": "completed",
  "errorMessage": null,
  "retries": 0,
  "maxRetries": 5,
  "createdAt": "2025-11-14T12:12:38.413Z",
  "updatedAt": "2025-11-14T12:12:41.957Z",
  "result": {
    "data": {
      "caseFile": {
        "id": 1234
      },
      "signingLinks": [
        {
          "name": "Test User",
          "role": "Signer",
          "signerId": 2044563,
          "signOrder": [
            0
          ],
          "signingLink": "https://sandbox.penneo.com/signing/ABCDE-12345-FGHIJ-KLMNO-56789-PQRST"
        }
      ]
    },
    "errors": null,
    "success": true
  }
}

Error Handling

HTTP Status Codes

Status CodeDescriptionAction
200 OKJob found and status returnedProcess the job status
404 Not FoundJob not found or hash mismatchVerify UUID and payload hash
429 Too Many RequestsRate limit exceeded (>20 req/min)Wait before retrying
500 Internal Server ErrorServer errorRetry after a delay

Usage Guide

Polling Strategy

Implement a polling strategy to check job status without overwhelming the API.

Recommended Polling Intervals

Job StatusPolling IntervalMax Duration
pending5 seconds10 minutes, but we expect much faster
processing3-5 seconds5 minutes, but we expect much faster
completedStop polling-
failedStop polling-

Best Practices

1. Store Job Details Immediately

After creating a case file, immediately store the uuid and payloadHash as they will not be retrievable later.

2. Set Reasonable Timeouts and respect the Rate Limits

Don't poll indefinitely. Set a reasonable timeout:

  • Recommended timeout: at least 10 minutes until you see a status change but usually you will have the CaseFile created or failed in a few seconds depending on the number of signers and documents.
  • Maximum attempts: 20 polls / minute / CaseFile. After exhausting the limit, 429 HTTP responses will be returned.
  • Polling interval: recommend minimum 3 seconds to fit the maximum attempts but we count the number of attempts per minute, so it is at your discretion if you want to exponentially increased the interval to fit the 20 requests / minute.

Security Considerations

Protect Credentials

The uuid and payloadHash together provide access to job status:

  • Store them securely
  • Don't share payload hashes/uuid publicly

Integration Workflow

Complete Case File Creation + Status Check Flow

1. Create Case File
   ↓
2. Receive Job UUID + Payload Hash
   ↓
3. Store UUID + Hash in Database/Your own storage
   ↓
4. Start Polling (every 3-5 seconds)
   ↓
5. Check Job Status
   ↓
   ├─→ pending/processing → Continue Polling
   ├─→ completed → Process Result
   └─→ failed → Handle Error
flowchart TD
    A[Start: createAndMonitorCaseFile] --> B[Step 1: Create Case File]
    B --> C{Is createResponse.status 'handled'?}
    C -- No --> D[Throw Error: Failed to create case file]
    C -- Yes --> E[Step 2: Store job details in database]
    E --> F[Step 3 Poll job status call public/status every 3s]
    
    %% Polling loop
    F --> G{completedJob.jobStatus}
    G -- pending/processing --> H[Wait 3 seconds then poll again]
    H --> F

    G -- completed --> I[Update job as 'completed' in database]
    I --> J[Return completedJob.result]

    G -- failed --> K[Update job as 'failed' in database]
    K --> L[Return failedJob.result]
Language
URL
Click Try It! to start a request and see the response here!