> ## Documentation Index
> Fetch the complete documentation index at: https://docs.agribackup.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Retrieve Async Job Status and Compliance Metrics

> Poll the status and real-time processing metrics of an asynchronous compliance job, including phase, record counts, Hedera transaction ID, and structured errors.

Because satellite deforestation screening, Hedera DLT anchoring, and TRACES NT submission all take time, AgriBackup ingestion endpoints return a `202 Accepted` response with a `jobId` rather than blocking until processing completes. You use this endpoint to poll that job and determine its current lifecycle phase, track how many records have been processed, inspect any structured errors, and retrieve the final cryptographic proofs once anchoring is complete. Your integration should poll this endpoint at a reasonable interval (we recommend every 2–5 seconds) until the `phase` field returns `COMPLETED` or `FAILED`, at which point you can also listen for the corresponding `polygon.verified`, `shipment.linked`, or `job.failed` webhook event.

## Endpoint

`GET /api/v1/enterprise/eudr/jobs/{jobId}`

## Path Parameters

<ParamField path="jobId" type="string" required>
  The unique identifier of the asynchronous job to query. This value is returned in the `jobId` field of the `202 Accepted` response from ingestion endpoints. Example: `job_998877`
</ParamField>

## Response Fields

<ResponseField name="jobId" type="string" required>
  The unique asynchronous tracking job ID. Example: `job_998877`
</ResponseField>

<ResponseField name="organizationId" type="string" required>
  The organization ID that initiated the job. Example: `org_test_tier1_alpha`
</ResponseField>

<ResponseField name="phase" type="string" required>
  The current execution phase of the job. Possible values: `IMPORTING`, `ANALYZING`, `ANCHORING`, `GENERATING_CERTS`, `COMPLETED`, `FAILED`.
</ResponseField>

<ResponseField name="totalRecords" type="integer" required>
  Total number of records (polygons, batches, etc.) submitted for this job.
</ResponseField>

<ResponseField name="processedRecords" type="integer" required>
  Number of records that have been processed so far. Use `processedRecords / totalRecords` to compute progress.
</ResponseField>

<ResponseField name="failedRecords" type="integer" required>
  Number of records that failed processing. A non-zero value does not necessarily mean the entire job failed — inspect the `errors` array for details.
</ResponseField>

<ResponseField name="compliantUnits" type="integer" required>
  Number of units (polygons or batches) that passed EUDR compliance checks. Present in the response once `phase` is `COMPLETED`.
</ResponseField>

<ResponseField name="highRiskUnits" type="integer" required>
  Number of units flagged as high-risk during satellite analysis. These require additional due diligence before DDS submission.
</ResponseField>

<ResponseField name="merkleRootHash" type="string">
  The Hedera Merkle root hash for the anchored dataset. Present once the `ANCHORING` phase is complete. Example: `0xabc123...`
</ResponseField>

<ResponseField name="hederaTransactionId" type="string">
  The Hedera consensus transaction ID that provides immutable proof of anchoring. Example: `0.0.8713513@1713583200-000000000`
</ResponseField>

<ResponseField name="startedAt" type="integer" required>
  Unix epoch millisecond timestamp of when the job began processing.
</ResponseField>

<ResponseField name="completedAt" type="integer">
  Unix epoch millisecond timestamp of when the job reached a terminal state (`COMPLETED` or `FAILED`). `null` if still in progress.
</ResponseField>

<ResponseField name="errors" type="object[]" required>
  A structured array of error objects for records that failed during this job. An empty array indicates no failures.
</ResponseField>

<ResponseField name="errors[].code" type="string" required>
  Programmatic error code for automated branch logic. Example: `POLYGON_OVERLAP`
</ResponseField>

<ResponseField name="errors[].field" type="string">
  The specific record identifier or field that caused the failure. Example: `polygon_id_123`
</ResponseField>

<ResponseField name="errors[].reason" type="string" required>
  Human-readable description of the failure. Example: `Polygon 12 overlaps protected park space`
</ResponseField>

## Job Phase Lifecycle

```
IMPORTING → ANALYZING → ANCHORING → GENERATING_CERTS → COMPLETED
                                                      ↘ FAILED
```

| Phase              | Description                                                       |
| ------------------ | ----------------------------------------------------------------- |
| `IMPORTING`        | Records are being ingested and validated                          |
| `ANALYZING`        | Copernicus Sentinel-2 satellite deforestation analysis is running |
| `ANCHORING`        | Compliance proofs are being anchored on Hedera Hashgraph          |
| `GENERATING_CERTS` | Cryptographic compliance certificates are being generated         |
| `COMPLETED`        | All records processed; `hederaTransactionId` is available         |
| `FAILED`           | Job terminated with errors; inspect the `errors` array            |

## Error Responses

| Status | Code           | Description                                           |
| ------ | -------------- | ----------------------------------------------------- |
| `401`  | `UNAUTHORIZED` | Missing or invalid `X-API-Key`                        |
| `403`  | `FORBIDDEN`    | Your API key does not have job monitoring permissions |
| `404`  | `NOT_FOUND`    | No job found for the provided `jobId`                 |

<CodeGroup>
  ```python Python theme={null}
  import agribackup
  from agribackup.rest import ApiException
  import time

  configuration = agribackup.Configuration(host="https://live.agribackup.com/api/v1")
  configuration.api_key['ApiKeyAuth'] = 'sk_live_YOUR_API_KEY'

  JOB_ID = "job_998877"
  TERMINAL_PHASES = {"COMPLETED", "FAILED"}

  with agribackup.ApiClient(configuration) as api_client:
      api_instance = agribackup.EnterpriseJobsApi(api_client)
      while True:
          try:
              job = api_instance.get_job_status(job_id=JOB_ID)
              print(f"Phase: {job.phase} | Processed: {job.processed_records}/{job.total_records}")

              if job.phase in TERMINAL_PHASES:
                  if job.phase == "COMPLETED":
                      print(f"Job complete! Hedera TX: {job.hedera_transaction_id}")
                      print(f"Compliant: {job.compliant_units} | High Risk: {job.high_risk_units}")
                  else:
                      print(f"Job failed with {len(job.errors)} error(s):")
                      for err in job.errors:
                          print(f"  [{err.code}] {err.field}: {err.reason}")
                  break

              time.sleep(3)
          except ApiException as e:
              print("Exception when calling get_job_status: %s\n" % e)
              break
  ```

  ```javascript Node.js theme={null}
  import { AgriBackupClient } from '@agribackup/sdk';

  const client = new AgriBackupClient({
    apiKey: 'sk_live_YOUR_API_KEY',
    baseUrl: 'https://live.agribackup.com/api/v1'
  });

  async function pollJobStatus(jobId) {
    const terminalPhases = new Set(['COMPLETED', 'FAILED']);

    while (true) {
      try {
        const job = await client.jobs.getJobStatus({ jobId });
        console.log(`Phase: ${job.phase} | Processed: ${job.processedRecords}/${job.totalRecords}`);

        if (terminalPhases.has(job.phase)) {
          if (job.phase === 'COMPLETED') {
            console.log(`Job complete! Hedera TX: ${job.hederaTransactionId}`);
            console.log(`Compliant: ${job.compliantUnits} | High Risk: ${job.highRiskUnits}`);
          } else {
            console.log(`Job failed with ${job.errors.length} error(s):`);
            job.errors.forEach(err => {
              console.log(`  [${err.code}] ${err.field}: ${err.reason}`);
            });
          }
          break;
        }

        await new Promise(resolve => setTimeout(resolve, 3000));
      } catch (error) {
        console.error('Error polling job status:', error);
        break;
      }
    }
  }

  pollJobStatus('job_998877');
  ```

  ```bash cURL theme={null}
  curl --request GET \
    --url https://live.agribackup.com/api/v1/enterprise/eudr/jobs/job_998877 \
    --header 'X-API-Key: sk_live_YOUR_API_KEY'
  ```
</CodeGroup>

### Example Response (Completed Job)

```json theme={null}
{
  "jobId": "job_998877",
  "organizationId": "org_test_tier1_alpha",
  "phase": "COMPLETED",
  "totalRecords": 10,
  "processedRecords": 10,
  "failedRecords": 0,
  "compliantUnits": 10,
  "highRiskUnits": 0,
  "merkleRootHash": "0xabc123def456...",
  "hederaTransactionId": "0.0.8713513@1713583200-000000000",
  "startedAt": 1713583200000,
  "completedAt": 1713583215000,
  "errors": []
}
```
