> ## 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.

# Export Integration Workflow Guide

export const WorkflowDiagramTopNavASERPNoClick = () => {
  const diagram = `
%%{init: {"themeVariables": {"fontSize": "30px"}}}%%
flowchart LR

    WF1["Pleo APIs"]

    subgraph ERP["AS/ERP"]
    direction LR
        START1[" "]
        A["1.Determine Bookkeeping Method"] --> B["2.Apply Accounts Mapping"]
        B --> C["3.Apply Data Mapping"]
        C --> D["4.Transfer Attachments"]
        D --> E["5.Assign Accounting Period"]
        E --> F["6.Apply Posting Behaviour"]
    end

    WF2["Pleo APIs"]

    WF1 --> A
    F --> WF2

    %% Styling for wrapping
    style A white-space:normal
    style B white-space:normal
    style C white-space:normal
    style D white-space:normal
    style E white-space:normal
    style F white-space:normal

    %% Make subgraph transparent with black border
    style START1 fill:transparent,stroke:transparent,color:transparent
    style ERP fill:none,stroke:#000000
`;
  return <Mermaid chart={diagram} />;
};

export const UpdateAndCompleteExportJobDiagramNoClick = () => {
  const diagram = `
%%{init: {"themeVariables": {"fontSize": "14px"}}}%%
flowchart LR
    S1["1. Determine Final Outcome"] --> S2["2. Build Event Payload<br><br>"] --> S3["3. Send Event<br><br>"] --> S4["4. Handle Response<br><br>"]

style S1 white-space:normal
style S2 white-space:normal
style S3 white-space:normal
style S4 white-space:normal
`;
  return <Mermaid chart={diagram} />;
};

export const UpdateExportItemsDiagramNoClick = () => {
  const diagram = `
%%{init: {"themeVariables": {"fontSize": "22px"}}}%%
flowchart LR
    S1["1. Collect Results<br><br>"] --> S2["2. Build Update Payload"] --> S3["3. Batch Items<br><br>"] --> S4["4. Send Request<br><br>"] --> S5["5. Handle Response"]

style S1 white-space:normal
style S2 white-space:normal
style S3 white-space:normal
style S4 white-space:normal
style S5 white-space:normal
`;
  return <Mermaid chart={diagram} />;
};

export const FetchExportItemDataDiagramNoClick = () => {
  const diagram = `
%%{init: {"themeVariables": {"fontSize": "20px"}}}%%
flowchart LR
    S1["1. Fetch Full Export Item Data"] --> S2["2. Handle Pagination<br><br>"] --> S3["3. Validate Each Export Item"]
    S1 -.->|interrupted| S4["4. Handle Job Recovery"]
    S2 -.->|interrupted| S4
    S3 -.->|interrupted| S4

style S1 white-space:normal
style S2 white-space:normal
style S3 white-space:normal
style S4 white-space:normal
`;
  return <Mermaid chart={diagram} />;
};

export const RetrieveExportJobItemsDiagramNoClick = () => {
  const diagram = `
%%{init: {"themeVariables": {"fontSize": "22px"}}}%%
flowchart LR
    S1["1. Retrieve Export Items for Job"] --> S2["2. Handle Pagination"] --> S4["4. Store Items for Processing"] --> S5["5. Maintain Deterministic Processing Order"]
    S1 -.->|interrupted| S3["3. Handle Job Recovery"]
    S2 -.->|interrupted| S3
    S3 -.-> S4

style S1 white-space:normal
style S2 white-space:normal
style S3 white-space:normal
style S4 white-space:normal
style S5 white-space:normal
`;
  return <Mermaid chart={diagram} />;
};

export const PreExportValidationDiagramNoClick = () => {
  const diagram = `
%%{init: {"themeVariables": {"fontSize": "16px"}}}%%
flowchart LR
    S1["1. Validate AS Connectivity"] --> S2["2. Validate Company Context"] --> S3["3. Validate Bookkeeping Capability"]
    S3 -->|passes| S5["Validation Passed"]
    S1 -->|fails| S4["4. Fail Early"]
    S2 -->|fails| S4
    S3 -->|fails| S4

style S1 white-space:normal
style S2 white-space:normal
style S3 white-space:normal
style S4 white-space:normal
style S5 white-space:normal
`;
  return <Mermaid chart={diagram} />;
};

export const DetectAndStartExportJobsDiagramNoClick = () => {
  const diagram = `
%%{init: {"themeVariables": {"fontSize": "28px"}}}%%
flowchart LR
    S1["1. Detect Available Jobs"] --> S2["2. Confirm Eligible Job Status"] --> S3["3. Select Oldest Job"] --> S4["4. Verify Job Status"]
    S4 -->|pending| S5["5. Start Export Job"]
    S4 -->|in_progress| S7["7. Process Timely"]
    S5 --> S7
    S5 -->|conflict 422| S6["6. Handle Conflicts"]

style S1 white-space:normal
style S2 white-space:normal
style S3 white-space:normal
style S4 white-space:normal
style S5 white-space:normal
style S6 white-space:normal
style S7 white-space:normal
`;
  return <Mermaid chart={diagram} />;
};

export const WhatComesNext = ({children, href}) => <div className="mt-4">
    <a href={href} className="
        inline-flex items-center justify-center
        rounded-full
        bg-black text-white dark:bg-[#1f262b]
        px-5 py-2.5 text-sm font-medium
        no-underline border-0
        hover:bg-[#ffe6ea] dark:hover:bg-[#2b1f23]
        hover:text-black
        transition-colors
      ">
      {children} →
    </a>
  </div>;

export const RecommendedCallout = ({title, children, icon = "⭐"}) => <div style={{
  backgroundColor: 'var(--recommended-bg)',
  borderLeft: '4px solid #f8f1ac',
  borderRadius: '10px',
  padding: '18px 22px',
  marginBottom: '20px',
  boxShadow: '1px 1px 3px rgba(0,0,0,0.06)'
}}>
    <div style={{
  display: 'flex',
  alignItems: 'flex-start',
  gap: '14px'
}}>
      <span style={{
  fontSize: '22px',
  lineHeight: '1',
  flexShrink: 0
}}>
        {icon}
      </span>
      <div>
        {title && <div style={{
  fontSize: '16px',
  fontWeight: 600,
  color: 'var(--recommended-title)',
  marginBottom: '6px'
}}>
            {title}
          </div>}
        <div style={{
  fontSize: '14px',
  lineHeight: 1.65
}}>
          {children}
        </div>
      </div>
    </div>
  </div>;

<RecommendedCallout title="Recommended Workflow">
  This guide covers [Integration Level 1](/docs/current/getting-started/accounting-integrations-overview), which enables exporting expenses and attachments. It is suitable for basic accounting workflows.
</RecommendedCallout>

## What You'll Have Built

After implementing this workflow:

* Export Jobs are detected and processed reliably, with sequential ordering guaranteed.
* Expenses are validated, mapped, and recorded correctly in the Accounting System.
* Attachments and bookkeeping logic are handled consistently for every Export Item.
* Export outcomes, both success and failure, are clearly reported back to Pleo for each item.
* The integration aligns with Pleo's export guarantees and bookkeeping model.

## Who This Guide Is For

This guide is intended for:

* Integration developers
* Solution architects
* Accounting platform integrators

It focuses on **workflow understanding**, not implementation details.

## Before You Start

You should be familiar with:

* Pleo's [supported authentication](/docs/current/integration-design/auth/integration-design-auth-overview#authentication-policy-overview) methods.
* The [Export Lifecycle](/docs/current/platform/exports/lifecycle)

## Export Workflow Overview

When a bookkeeper queues expenses in Pleo's Web App and clicks Export, Pleo creates an Export Job, a batch of Export Items (individual expenses), ready to be processed by the integration. The integration detects this job, verifies the Accounting System is ready, retrieves the items, and processes each one into an accounting entry. Once all items are processed, the integration reports the outcome for each item back to Pleo and sends a final event to close the job.

Steps 1–4 and 6–7 interact directly with Pleo's Export API. Step 5 (Process & Record Export Items) takes place inside the Accounting System or ERP and is covered in detail in the [AS/ERP Processing Workflow Guide](/docs/current/guides/accounting-system-processing-workflow-guide). Each step produces outputs that the next step depends on.

```mermaid theme={null}
%%{init: {"themeVariables": {"fontSize": "28px"}}}%%
flowchart LR

  WEBAPP["Queue Export Items in Pleo's Web App"]

subgraph Pleo["Pleo APIs"]
    START1[" "]
    A["1.Detect & Start Export Jobs"] --> B["2.Perform Pre-Export Validation"]
    B --> C["3.Retrieve Export Job Items for Processing"]
    C --> D["4.Fetch Export Item Data for Processing"]
end

subgraph ERP["AS/ERP"]
    START2[" "]
    E["5.Process & Record Export Items"]
end

subgraph Pleo2["Pleo APIs"]
    START3[" "]
    F["6.Update Export Items"] --> G["7.Update & Complete Export Job"]
end
WEBAPP --> A
D --> E
E --> F

click WEBAPP "/docs/current/how-tos/accounting-integrations/how-to-queue-export-items-in-ui"
click A "#1-detect-and-start-export-jobs"
click B "#2-perform-pre-export-validation"
click C "#3-retrieve-export-job-items-control-layer"
click D "#4-fetch-export-item-data-data-layer"
click E "#5-process-%26-record-export-items"
click F "#6-update-export-items"
click G "#7-update-%26-complete-export-job"

style A white-space:normal
style B white-space:normal
style C white-space:normal
style D white-space:normal
style E white-space:normal
style F white-space:normal
style G white-space:normal

style START1 fill:transparent,stroke:transparent,color:transparent
style START2 fill:transparent,stroke:transparent,color:transparent
style START3 fill:transparent,stroke:transparent,color:transparent
style Pleo fill:none,stroke:#000000
style ERP fill:none,stroke:#000000
style Pleo2 fill:none,stroke:#000000

```

***

## Steps

### 1. Detect and Start Export Jobs

#### Purpose

When Pleo creates an Export Job, the integration must detect it and mark it as started before any processing can begin. Detection can happen via a webhook notification (preferred, as Pleo triggers the integration the moment a job is created) or via scheduled polling. In either case, the integration selects the oldest pending job and sends a `started` event to Pleo's Export API, which transitions the job to `in_progress` and establishes the integration's exclusive processing rights.

On startup or after a connection loss, the integration may also detect an `in_progress` job it previously started. In this case, the `started` event is skipped and processing resumes from where it left off.

#### Input

* An Export Job created by Pleo when expenses are queued and exported via the Web App, with status `pending`
* A webhook notification (`export-job.created`) or a scheduled polling trigger that initiates detection
* On recovery: an Export Job already in `in_progress` state that was previously started by this integration but not completed

#### Workflow Process

<DetectAndStartExportJobsDiagramNoClick />

#### Output

* The oldest eligible Export Job identified and selected for processing
* The Export Job marked as `in_progress` via the `started` event, confirming the integration has taken responsibility for this batch
* No other worker or integration instance will start the same job

#### Why It Matters

Export Jobs must be processed one at a time and in order to prevent accounting inconsistencies. Starting a job is the mechanism that establishes this exclusivity: only one integration instance can successfully send the `started` event for a given job. This prevents duplicate accounting entries from being created if multiple workers are briefly running simultaneously, and creates an auditable record of when processing began.

#### Integration Design

If you're an integration developer or architect, read the [Detect & Start Export Jobs](/docs/current/integration-design/exports/integration-design-exports-detect-and-start-export-jobs) integration design doc before implementing this step. It covers the detection mechanisms (webhook, polling, ad hoc trigger), sequencing requirements, how concurrent start conflicts are handled, and the rules for recovery scenarios.

#### Step-by-Step Instructions

When you're ready to start implementing, follow the step-by-step instructions in the accompanying How-to article.

<WhatComesNext href="/docs/current/how-tos/accounting-integrations/how-to-detect-and-start-export-jobs-for-as-erp-processing">
  How to Detect and Start Export Jobs for Processing
</WhatComesNext>

***

### 2. Perform Pre-Export Validation

#### Purpose

Before any Export Items are processed, the integration verifies that the Accounting System is reachable and correctly configured for this Export Job. This check happens immediately after the job is started and before any accounting data is touched. If the Accounting System is unavailable, the integration is misconfigured, or the required bookkeeping method is not supported, the job is marked as failed with a clear, actionable reason, and processing stops cleanly.

#### Input

* The started Export Job (status `in_progress`), including metadata such as `companyId` and `vendorBasedBookkeeping`
* A valid connection to the target Accounting System, with credentials and required permissions in place

#### Workflow Process

<PreExportValidationDiagramNoClick />

#### Output

* Validation passed: the integration proceeds to retrieve Export Items for processing
* Validation failed: the Export Job is marked `failed` with a specific `failureReason` and `failureReasonType`, and processing stops. The failure is surfaced to the user in Pleo's Web App.

#### Why It Matters

Failing early is far less costly than failing mid-export. If validation were skipped and a problem were discovered halfway through processing, the Accounting System could be left in an inconsistent state, with some entries created and others missing. Pre-export validation ensures the integration either proceeds with confidence or fails cleanly before any accounting data is written.

#### Integration Design

If you're an integration developer or architect, read the [Perform Pre-Export Validation](/docs/current/integration-design/exports/integration-design-exports-pre-export-validation) integration design doc before implementing this step. It covers the required validation checks, the supported failure reason types, and when to fail the job versus when to handle issues at the item level.

#### Step-by-Step Instructions

When you're ready to start implementing, follow the step-by-step instructions in the accompanying How-to article.

<WhatComesNext href="/docs/current/how-tos/accounting-integrations/how-to-perform-pre-export-validation-for-as-erp-processing">
  How to Perform Pre-Export Validation
</WhatComesNext>

***

### 3. Retrieve Export Job Items (Control Layer)

#### Purpose

Before fetching full accounting payloads, the integration retrieves a lightweight control list of all Export Job Items. Each item includes its unique `accountingEntryId` and current processing status (`pending`, `in_progress`, `successful`, or `failed`), but not the full expense data. This establishes the processing scope: which items exist in this job and which still require work.

The control layer is also the recovery mechanism. If the integration restarts mid-export, it re-fetches this list, filters for items still in `pending` state, and resumes from where it left off without reprocessing already-completed items. Items in `in_progress` state are intentionally excluded from the recovery filter. They may have been partially processed before the crash, and retrying them risks creating duplicate accounting entries.

#### Input

* The Export Job ID (`jobId`) from the started Export Job
* On recovery: awareness that the job is already `in_progress`, triggering a re-fetch to determine remaining work

#### Workflow Process

<RetrieveExportJobItemsDiagramNoClick />

#### Output

* A complete, paginated list of all Export Job Items with their current statuses
* A defined processing scope: the set of items still requiring work, typically those with status `pending`
* A deterministic processing order established, ensuring consistent and auditable execution across retries

#### Why It Matters

Separating the control layer from the data layer keeps the workflow modular and resilient. The integration knows exactly what it needs to process before making heavier API calls for full payloads. This prevents partial processing, enables the workflow to resume safely after interruptions, and ensures no item is missed or inadvertently processed twice.

#### Integration Design

If you're an integration developer or architect, read the [Retrieve Export Job Items](/docs/current/integration-design/exports/integration-design-exports-fetch-export-items-control-layer) integration design doc before implementing this step. It covers the control layer vs data layer distinction, pagination requirements, the recovery strategy, and how deterministic ordering should be implemented.

#### Step-by-Step Instructions

When you're ready to start implementing, follow the step-by-step instructions in the accompanying How-to article.

<WhatComesNext href="/docs/current/how-tos/accounting-integrations/how-to-retrieve-export-job-items-for-as-erp-processing">
  How to Retrieve Export Job Items
</WhatComesNext>

***

### 4. Fetch Export Item Data (Data Layer)

#### Purpose

With the processing scope established, the integration retrieves the full accounting payload for each Export Item. This is the data layer: each item contains all the information needed to create an accounting entry: amounts, dates, GL account codes, vendor details, tax codes, dimensions, attachment references, and the bookkeeping method to apply.

This step is intentionally separate from the control layer. The integration first knows what to process (Step 3), then retrieves the data needed to process it (Step 4), keeping workflow control and data retrieval cleanly decoupled.

#### Input

* The Export Job ID (`jobId`)
* The list of `accountingEntryId` values from the control layer (Step 3), defining which items require full data retrieval

#### Workflow Process

<FetchExportItemDataDiagramNoClick />

#### Output

* Full accounting payloads retrieved for each Export Item in the processing scope
* Each payload validated to confirm it contains the data required for the intended bookkeeping method
* Items ready to be passed into the Accounting System processing workflow

#### Why It Matters

The Export Item payload contains everything the integration needs to create a correct accounting entry. Fetching it as a separate step, after the processing scope is established, means the integration can validate completeness before committing to any accounting operations. It also means items can be handled individually, so a problem with one item's data does not prevent others from being processed.

#### Integration Design

If you're an integration developer or architect, read the [Fetch Export Item Data](/docs/current/integration-design/exports/integration-design-exports-fetch-export-items-data-layer) integration design doc before implementing this step. It covers the data layer structure, the fields available in the Export Item payload, pagination, validation requirements, and how to handle recovery.

#### Step-by-Step Instructions

When you're ready to start implementing, follow the step-by-step instructions in the accompanying How-to article.

<WhatComesNext href="/docs/current/how-tos/accounting-integrations/how-to-fetch-export-item-data-for-as-erp-processing">
  How to Fetch Export Item Data
</WhatComesNext>

***

### 5. Process & Record Export Items

#### Purpose

This step is where the integration processes each Export Item and records it into the Accounting System. For each item, the integration resolves the bookkeeping method, applies accounts and data mapping, handles attachments, assigns the correct accounting period, and creates the entry using the configured posting behaviour. Each Export Item is processed independently: a failure on one item does not stop the others from being processed.

#### Input

* Full Export Item payloads from Step 4
* Integration configuration: accounts mapping, posting behaviour, and closed period strategy
* A valid connection to the Accounting System

#### Workflow Process

<WorkflowDiagramTopNavASERPNoClick />

#### Output

* Draft or finalised accounting entries created in the Accounting System for each successfully processed item
* Attachments uploaded and linked to their corresponding accounting entries where applicable
* A success or failure result captured for each item, ready to be reported back to Pleo in Step 6

#### Why It Matters

This is where Pleo expense data becomes accounting records. Getting this step right, with correct GL accounts, accurate data mapping, and appropriate posting status, determines the quality and auditability of the Accounting System. Per-item failure isolation ensures that a single problematic expense does not block the rest of the batch.

#### Step-by-Step Instructions

This step is covered in full in the AS/ERP Processing Workflow Guide, which walks through bookkeeping method resolution, accounts mapping, data mapping, attachment handling, accounting period assignment, and posting behaviour.

<WhatComesNext href="/docs/current/guides/accounting-system-processing-workflow-guide">
  AS/ERP Processing Workflow Guide
</WhatComesNext>

***

### 6. Update Export Items

#### Purpose

After each Export Item is processed in the Accounting System, the integration reports the outcome back to Pleo. For successful items, this includes the external ID and URL of the entry created in the AS, providing a direct link between the Pleo expense and its accounting record. For failed items, this includes a clear, actionable failure reason. Pleo uses these updates to surface results to bookkeepers in the Export Queue.

Items are sent in batches of up to 100 per API request.

#### Input

* The success or failure result for each processed Export Item, captured during Step 5
* The `accountingEntryId` for each item, from the control layer (Step 3)
* For successful items: the external ID and URL assigned by the Accounting System
* For failed items: the failure reason and reason type, derived from the Accounting System's response

#### Workflow Process

<UpdateExportItemsDiagramNoClick />

#### Output

* Every Export Item in the batch updated with a final status: `successful` or `failed`
* Successful items linked to their corresponding entries in the Accounting System via external ID and URL
* Failed items recorded with actionable error information, visible to bookkeepers for review and correction

#### Why It Matters

Reporting item-level outcomes back to Pleo is what makes the export visible and auditable for users. Without these updates, bookkeepers cannot see which expenses exported successfully, which failed, or why. Accurate and timely updates also enable Export Job completion: the final step cannot be triggered until all items have a recorded outcome.

#### Integration Design

If you're an integration developer or architect, read the [Update Export Items](/docs/current/integration-design/exports/integration-design-exports-update-export-items) integration design doc before implementing this step. It covers the supported status values, the required and optional fields for successful and failed items, and the failure reason type mappings for common Accounting System errors.

#### Step-by-Step Instructions

When you're ready to start implementing, follow the step-by-step instructions in the accompanying How-to article.

<WhatComesNext href="/docs/current/how-tos/accounting-integrations/how-to-update-export-items-for-as-erp-processing">
  How to Update Export Items
</WhatComesNext>

***

### 7. Update & Complete Export Job

#### Purpose

Once all Export Items have been updated with their final statuses, the integration sends a completion event to close the Export Job. The event reflects the aggregated outcome of the entire batch: `completed` if all items succeeded, `completed_with_errors` if some failed, or `failed` if all items failed. This is the signal to Pleo that processing is finished and the full export result can be surfaced to users.

#### Input

* The final processing status of all Export Items, established in Step 6
* The Export Job ID (`jobId`)

#### Workflow Process

<UpdateAndCompleteExportJobDiagramNoClick />

#### Output

* The Export Job marked with its final status: `completed`, `completed_with_errors`, or `failed`
* Export results fully visible to users in Pleo's Export Queue, including per-item success and failure details
* The export lifecycle complete: no further updates can be made to this job

#### Why It Matters

Completing the Export Job is the final confirmation that the integration has finished processing the batch. It unlocks the user-visible export status in Pleo's Web App, allowing bookkeepers to review results, investigate failures, and resubmit if necessary. It also provides a clear audit trail of when processing ended and what the overall outcome was for the batch.

#### Integration Design

If you're an integration developer or architect, read the [Update & Complete Export Job](/docs/current/integration-design/exports/integration-design-exports-update-and-complete-export-job) integration design doc before implementing this step. It covers the supported completion events, the conditions under which each is used, and the failure reason guidance for job-level failures.

#### Step-by-Step Instructions

When you're ready to start implementing, follow the step-by-step instructions in the accompanying How-to article.

<WhatComesNext href="/docs/current/how-tos/accounting-integrations/how-to-update-and-complete-export-job-for-as-erp-processing">
  How to Update & Complete the Export Job
</WhatComesNext>

***

## What Comes Next?

After implementing the Export Expenses workflow, you can extend your integration with:

* [Tags Sync](/docs/current/integration-design/accounting-integrations/imports/tags/integration-design-tags-overview)

***

## Related Reading

* [Exports Overview](/docs/current/integration-design/exports/integration-design-exports-overview)
* [Exports Lifecycle](/docs/current/platform/exports/lifecycle)

***
