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

# Detect & Start Export Jobs

export const IntegrationDesignExportsDetectAndStartExportJobsDiagram = () => {
  const [isDark, setIsDark] = useState(false);
  useEffect(() => {
    const check = () => setIsDark(document.documentElement.classList.contains("dark"));
    check();
    const observer = new MutationObserver(check);
    observer.observe(document.documentElement, {
      attributes: true,
      attributeFilter: ["class"]
    });
    return () => observer.disconnect();
  }, []);
  const nodeFill = isDark ? "#212222" : "#EEF4F4";
  const nodeStroke = isDark ? "#848989" : "#212222";
  const nodeTextStyle = isDark ? ",color:#EEF4F4" : ",color:#131414";
  const linkStyle = isDark ? "" : "linkStyle default stroke:#848989,stroke-width:1px;";
  const themeVariables = {
    fontSize: "12px",
    ...isDark ? {} : {
      edgeLabelBackground: "#FAFCFC"
    }
  };
  const diagram = `
%%{init: {"themeVariables": ${JSON.stringify(themeVariables)}}}%%
flowchart TD
    A[Detect Export Jobs] --> B[Filter eligible jobs]
    B --> C[Select oldest job]
    C --> D[Start job #40;started event#41;]
    D --> E[Begin processing]

   style A white-space:normal,fill:${nodeFill},stroke:${nodeStroke}${nodeTextStyle}
   style B white-space:normal,fill:${nodeFill},stroke:${nodeStroke}${nodeTextStyle}
   style C white-space:normal,fill:${nodeFill},stroke:${nodeStroke}${nodeTextStyle}
   style D white-space:normal,fill:${nodeFill},stroke:${nodeStroke}${nodeTextStyle}
   style E white-space:normal,fill:${nodeFill},stroke:${nodeStroke}${nodeTextStyle}
${linkStyle}
`;
  return <Mermaid chart={diagram} />;
};

export const RememberCallout = ({title, children}) => <div className="callout-box callout-remember">
    <div className="callout-row">
      <span className="callout-icon">
        <svg width="22" height="22" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" fill="currentColor"><path d="M229.66,98.34,172.39,155.8c11.46,22.93-1.72,45.86-10.11,57a8,8,0,0,1-12,.83L42.34,105.76A8,8,0,0,1,43,93.85c29.65-23.92,57.4-10,57.4-10l57.27-57.46a8,8,0,0,1,11.31,0L229.66,87A8,8,0,0,1,229.66,98.34Z" opacity="0.2" /><path d="M235.32,81.37,174.63,20.69a16,16,0,0,0-22.63,0L98.37,74.49c-10.66-3.34-35-7.37-60.4,13.14a16,16,0,0,0-1.29,23.78L85,159.71,42.34,202.34a8,8,0,0,0,11.32,11.32L96.29,171l48.29,48.29A16,16,0,0,0,155.9,224c.38,0,.75,0,1.13,0a15.93,15.93,0,0,0,11.64-6.33c19.64-26.1,17.75-47.32,13.19-60L235.33,104A16,16,0,0,0,235.32,81.37ZM224,92.69h0l-57.27,57.46a8,8,0,0,0-1.49,9.22c9.46,18.93-1.8,38.59-9.34,48.62L48,100.08c12.08-9.74,23.64-12.31,32.48-12.31A40.13,40.13,0,0,1,96.81,91a8,8,0,0,0,9.25-1.51L163.32,32,224,92.68Z" /></svg>
      </span>
      <div>
        {title && <div className="callout-title">
            {title}
          </div>}
        <div className="callout-body">
          {children}
        </div>
      </div>
    </div>
  </div>;

This page describes how integrations must:

1. **Detect Export Jobs** that are ready for processing
2. **Start a single Export Job** to begin processing

This is the **first step** in the Export Integration Workflow.

## Implementation

See the corresponding how-to article for API usage and step-by-step instructions:

* [How to Detect and Start Export Jobs for Processing](/docs/current/how-tos/accounting-integrations/how-to-detect-and-start-export-jobs-for-as-erp-processing)

## Conceptual Model

Export processing begins in two distinct phases:

| Phase     | Responsibility                                |
| --------- | --------------------------------------------- |
| Detection | Identify Export Jobs available for processing |
| Starting  | Take ownership of a single Export Job         |

These phases must remain **logically separate**:

* Detection **must not modify state**
* Starting **is the first state-changing action**

## Detect Export Jobs

### Purpose

Detection identifies Export Jobs that are ready to be processed.

It must:

* Detect newly created Export Jobs
* Identify jobs eligible for processing
* Avoid modifying job state

### Detection Mechanisms

Integrations may use one of the following:

#### Webhooks (preferred)

* Subscribe to `export-job.created`
* Trigger detection when event is received

#### Polling (fallback)

* Periodically call `GET /v3/export-jobs`
* Use controlled intervals (e.g. every few minutes)

#### Ad Hoc Trigger (optional)

* Exposes a user-initiated action (e.g. a button in the integration UI) that immediately runs the same polling flow as schedule polling
* Used **in combination with** scheduled polling, not as a standalone mechanism
* Useful when a user wants to check for pending jobs without waiting for the next scheduled interval

### Eligible Job States

The statuses to include depend on context:

| Context                 | Statuses                    | Reason                                            |
| ----------------------- | --------------------------- | ------------------------------------------------- |
| Normal operation        | `pending`                   | Job has not yet been started by any integration   |
| Recovery / reconnection | `pending` and `in_progress` | Integration may have an interrupted job to resume |

In normal operation, only `pending` jobs should be fetched. An `in_progress` job found during recovery indicates a previously interrupted workflow: the integration should resume it without sending a `started` event again.

### Sequential Processing Requirement

When multiple jobs exist:

* Always select the **oldest job**
* Process jobs **one at a time**
* Do **not** process jobs in parallel

This prevents:

* race conditions
* duplicate exports
* inconsistent accounting state

<RememberCallout title="Remember">
  Export Jobs only exist after expenses have been [queued](/docs/current/how-tos/accounting-integrations/how-to-queue-export-items-in-ui) in Pleo’s Web App.
</RememberCallout>

## Start Export Job

### Purpose

Starting an Export Job:

* Signals that the integration is starting processing
* Prevents multiple workers processing the same job
* Establishes ownership and traceability

### When to Start

An Export Job must only be started after:

* The job has been discovered
* Pre-processing checks (e.g. validation readiness) have completed

Starting too early can lead to:

* failed exports
* stuck jobs
* inconsistent state

### How Starting Works

Starting only applies to `pending` jobs. If the job is already `in_progress` (recovery scenario), skip the `started` event and resume from pre-export validation.

To start a `pending` job:

1. Select the **oldest eligible job**
2. Send a `started` event via:

```json theme={null}
{
  "event": "started",
  "jobId": "<jobId>"
}
```

## Result of Starting

After a successful start:

* Job status transitions to `in_progress`
* The integration becomes responsible for processing
* Export Items can now be fetched

## Concurrency & Conflict Handling

Integrations are designed to run a single export worker, but infrastructure doesn't always guarantee this. Rolling deployments, double-firing scheduled jobs, or a restarting worker can briefly produce two instances that both attempt to start the same job. This is an edge case, not an intended design pattern.

The `started` event acts as an atomic lock: only one instance can successfully start a given job. If starting fails due to a status conflict:

* Treat this as expected behaviour
* Do not retry aggressively
* Restart detection and select the next eligible job

## Key Rules

* Detection must be read-only
* Only one job may be processed at a time. Integrations should run a single export worker; the `started` event enforces this at the API level as a safety net for edge cases such as rolling deployments or double-firing schedulers
* Always process the oldest eligible job
* In normal operation, only `pending` jobs are eligible for detection
* Starting is the first state-changing action and only applies to `pending` jobs
* Starting must be done using the `started` event
* In recovery, an `in_progress` job must be resumed without re-sending the `started` event

## Processing Order

<IntegrationDesignExportsDetectAndStartExportJobsDiagram />

## Upstream Dependencies

* Export Items queued in Pleo Web App
* Integration authentication configured
* Webhooks, scheduled polling, or ad hoc trigger implemented

## Downstream Dependencies

* Pre-export validation
* Export Item retrieval
* AS/ERP processing workflow

***

## What Comes Next?

* [Perform Pre-Export Validation](/docs/current/integration-design/exports/integration-design-exports-pre-export-validation)

***

## Related Reading

* [How to Detect and Start Export Jobs for Processing](/docs/current/how-tos/accounting-integrations/how-to-detect-and-start-export-jobs-for-as-erp-processing)
* [Export Integration Workflow Guide](/docs/current/guides/export-integration-workflow-guide)
* [AS/ERP Processing Workflow Guide](/docs/current/guides/accounting-system-processing-workflow-guide)

***
