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

# Vendor Creation Overview

> Learn how Vendor Creation turns a new vendor added directly in Pleo into a real record in your Accounting System, ready for bookkeeping.

export const VendorCreationRelationshipDiagram = () => {
  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" : "#E1E6E6";
  const nodeTextStyle = isDark ? ",color:#EEF4F4" : ",color:#131414";
  const outerFill = "transparent";
  const subgraphFill = isDark ? "#131414" : "#ffffff";
  const subgraphStroke = isDark ? "#848989" : "#6B7070";
  const subgraphTextStyle = isDark ? ",color:#EEF4F4" : ",color:#131414";
  const pleoSubgraphTextStyle = ",color:#FEB6FE";
  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

    Bookkeeper["Bookkeeper"]

    subgraph pleo["Pleo"]
        draft["Vendor<br>(DRAFT)"]
        active["Vendor<br>(ACTIVE)"]
    end

    subgraph integration["Integration"]
        detect["Detect new<br>DRAFT Vendor"]
        create["Create Vendor<br>in AS"]
    end

    subgraph AS_out["Accounting System"]
        record["Vendor record"]
    end

    Bookkeeper -->|"adds a new vendor"| draft
    draft -->|"v1.vendor.created<br>(or 5-min poll)"| detect
    detect --> create
    create -->|"vendor created"| record
    create -.->|"code + externalId<br>returned via :activate"| active

    style Bookkeeper white-space:normal,fill:${outerFill},stroke:${nodeStroke}${nodeTextStyle}
    style draft white-space:normal,fill:${nodeFill},stroke:${nodeStroke}${nodeTextStyle}
    style active white-space:normal,fill:${nodeFill},stroke:${nodeStroke}${nodeTextStyle}
    style detect white-space:normal,fill:${nodeFill},stroke:${nodeStroke}${nodeTextStyle}
    style create white-space:normal,fill:${nodeFill},stroke:${nodeStroke}${nodeTextStyle}
    style record white-space:normal,fill:${nodeFill},stroke:${nodeStroke}${nodeTextStyle}
    style pleo fill:${subgraphFill},stroke:${subgraphStroke}${pleoSubgraphTextStyle}
    style integration fill:${subgraphFill},stroke:${subgraphStroke}${subgraphTextStyle}
    style AS_out fill:${subgraphFill},stroke:${subgraphStroke}${subgraphTextStyle}
${linkStyle}
`;
  return <Mermaid chart={diagram} />;
};

export const VendorCreationDirectionDiagram = () => {
  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" : "#E1E6E6";
  const nodeTextStyle = isDark ? ",color:#EEF4F4" : ",color:#131414";
  const outerFill = "transparent";
  const subgraphFill = isDark ? "#131414" : "#ffffff";
  const subgraphStroke = isDark ? "#848989" : "#6B7070";
  const subgraphTextStyle = isDark ? ",color:#EEF4F4" : ",color:#131414";
  const pleoSubgraphTextStyle = ",color:#FEB6FE";
  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

subgraph Pleo["Pleo"]
    source["Vendor (DRAFT)"]
    active["Vendor (ACTIVE)"]
end

subgraph Integration["Vendor Creation Integration"]
    create["Detection + Creation Logic"]
end

subgraph AS["Accounting System / ERP"]
    target["Vendor"]
end

source -->|"1. v1.vendor.created webhook (or poll)"| create
create -->|"2. creates record"| target
create -.->|"3. POST :activate<br>code + externalId"| active

style source white-space:normal,fill:${outerFill},stroke:${nodeStroke}${nodeTextStyle}
style active white-space:normal,fill:${nodeFill},stroke:${nodeStroke}${nodeTextStyle}
style create white-space:normal,fill:${nodeFill},stroke:${nodeStroke}${nodeTextStyle}
style target white-space:normal,fill:${nodeFill},stroke:${nodeStroke}${nodeTextStyle}
style Pleo fill:${subgraphFill},stroke:${subgraphStroke}${pleoSubgraphTextStyle}
style Integration fill:${subgraphFill},stroke:${subgraphStroke}${subgraphTextStyle}
style AS fill:${subgraphFill},stroke:${subgraphStroke}${subgraphTextStyle}
${linkStyle}
`;
  return <Mermaid chart={diagram} />;
};

export const NoteCallout = ({title, children}) => <div className="callout-box callout-note">
    <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="M208,104a79.86,79.86,0,0,1-30.59,62.92A24.29,24.29,0,0,0,168,186v6a8,8,0,0,1-8,8H96a8,8,0,0,1-8-8v-6a24.11,24.11,0,0,0-9.3-19A79.87,79.87,0,0,1,48,104.45C47.76,61.09,82.72,25,126.07,24A80,80,0,0,1,208,104Z" opacity="0.2" /><path d="M176,232a8,8,0,0,1-8,8H88a8,8,0,0,1,0-16h80A8,8,0,0,1,176,232Zm40-128a87.55,87.55,0,0,1-33.64,69.21A16.24,16.24,0,0,0,176,186v6a16,16,0,0,1-16,16H96a16,16,0,0,1-16-16v-6a16,16,0,0,0-6.23-12.66A87.59,87.59,0,0,1,40,104.49C39.74,56.83,78.26,17.14,125.88,16A88,88,0,0,1,216,104Zm-16,0a72,72,0,0,0-73.74-72c-39,.92-70.47,33.39-70.26,72.39a71.65,71.65,0,0,0,27.64,56.3A32,32,0,0,1,96,186v6h64v-6a32.15,32.15,0,0,1,12.47-25.35A71.65,71.65,0,0,0,200,104Zm-16.11-9.34a57.6,57.6,0,0,0-46.56-46.55,8,8,0,0,0-2.66,15.78c16.57,2.79,30.63,16.85,33.44,33.45A8,8,0,0,0,176,104a9,9,0,0,0,1.35-.11A8,8,0,0,0,183.89,94.66Z" /></svg>
      </span>
      <div>
        {title && <div className="callout-title">
            {title}
          </div>}
        <div className="callout-body">
          {children}
        </div>
      </div>
    </div>
  </div>;

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>;

<NoteCallout title="New to Pleo Vendors?">
  This page assumes you already know what a Vendor is, and that Vendor Creation is the flow you need. If you're not sure, start with [Supplier vs Vendor](/docs/current/platform/exports/vendors/supplier-vs-vendor-overview) for the underlying concept, then [Vendor Creation vs Vendor Sync](/docs/current/platform/exports/vendors/vendor-creation-vs-vendor-sync-overview) to confirm which of the two flows applies to your scenario.
</NoteCallout>

**Vendor Creation** lets a bookkeeper add a new vendor directly in Pleo, even if it doesn't exist in the Accounting System yet. The integration then creates the vendor in the Accounting System automatically.

This is the reverse of [Vendor Sync](/docs/current/platform/accounting-integrations/imports/vendors/vendor-sync-overview), which keeps Pleo up to date with vendors that already exist in the Accounting System.

## Purpose of Vendor Creation

**Vendor Creation** exists to:

* Let a bookkeeper add a new vendor directly in Pleo, without first creating it in the Accounting System
* Create that vendor reliably in the Accounting System, without manual data entry
* Let the bookkeeper use the vendor right away, instead of waiting for the next scheduled Vendor Sync
* Keep the vendor's identifiers (`code`, `externalId`) consistent across both systems once creation completes. The `:activate` call does not sync back descriptive fields, like name, country, currency, and tax and registration numbers. The [Vendor Sync workflow](/docs/current/platform/accounting-integrations/imports/vendors/vendor-sync-overview) picks those up later

## What Is a Draft Vendor?

A bookkeeper can add a vendor in Pleo before it exists in the Accounting System. Pleo then creates the record in `DRAFT` state.

| Vendor State | Meaning                                                                                                                                                                  |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `DRAFT`      | The vendor exists in Pleo only. It still needs to be created in the Accounting System.                                                                                   |
| `ACTIVE`     | The vendor exists in both Pleo and the Accounting System. It carries an `externalId`, and a `code` if the Accounting System assigns one. It can be used for bookkeeping. |
| `ARCHIVED`   | The vendor is no longer active in the Accounting System.                                                                                                                 |

A `DRAFT` vendor has no `code` or `externalId` yet. The Accounting System assigns both once it creates the record.

## Core Concept

<VendorCreationRelationshipDiagram />

The full chain is:

1. **Bookkeeper adds a vendor in Pleo:** the vendor is created in `DRAFT` state
2. **Pleo notifies the integration:** a `v1.vendor.created` webhook event is sent, or the integration detects the new `DRAFT` vendor via scheduled polling
3. **Integration creates the vendor in the Accounting System:** using the details from the `DRAFT` vendor
4. **Integration activates the vendor in Pleo:** passing back the `code` and `externalId` assigned by the Accounting System

<RememberCallout title="Remember">
  A vendor stays in `DRAFT` state in Pleo until the integration activates it. This does not stop a bookkeeper from tagging an expense with a `DRAFT` vendor and exporting it. Pleo does not block that export.

  <br />

  <br />

  The integration must still reject the export item, because the vendor has no `code` or `externalId` in the Accounting System yet. Report it back to Pleo with `failureReasonType: vendor_unknown`. See [How to Update Export Items](/docs/current/how-tos/accounting-integrations/how-to-update-export-items-for-as-erp-processing#2-build-update-payload).

  <br />

  <br />

  Activation is what confirms the vendor now exists in the Accounting System. Once activated, a later export of that vendor's expenses can succeed.
</RememberCallout>

## System Guarantees

| Guarantee                  | Description                                                                                                        |
| -------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| Source of Truth            | Pleo is the source of truth for a vendor's initial details (name, country, currency, tax and registration numbers) |
| Single Direction per Event | Each Vendor Creation event flows from Pleo into the Accounting System                                              |
| Identity Assignment        | The Accounting System always assigns `code` and `externalId`, never Pleo                                           |
| Non-Destructive Behaviour  | A `DRAFT` vendor is never deleted while it waits to be created. It stays visible until activated                   |
| Idempotency                | Re-processing the same `v1.vendor.created` event must not create duplicate vendors in the Accounting System        |

<RememberCallout title="Remember">
  To meet the Idempotency guarantee, the integration must store dedupe and retry data for each vendor. Index this data by the vendor's `id` field. You get this `id` from the `v1.vendor.created` webhook payload, or from `POST /v1/vendors:search` when polling for `DRAFT` vendors.

  <br />

  <br />

  Store three things:

  * The webhook `eventId` of each `v1.vendor.created` event you have already processed
  * A marker showing whether processing is currently in progress
  * The `code` and `externalId` assigned by the AS

  See [Failure Handling](/docs/current/integration-design/exports/vendors/integration-design-vendor-creation-create-and-activate#failure-handling) for more detail.

  <br />

  <br />

  Keep this data until the vendor no longer needs retries. That happens when the vendor reaches `ACTIVE` state and a later Vendor Sync run confirms it. It can also happen earlier, if you abandon the vendor under your own retry policy.

  <br />

  <br />

  Keep each webhook `eventId` for longer than that. Pleo retries a failed webhook delivery for up to about 27.5 hours before giving up (see [Webhook Notification Delivery Retries](/reference/webhooks/overview-webhooks#webhook-notification-delivery-retries) for the exact schedule). Retain the `eventId` for at least that long, even if it outlasts the vendor's own retry period. Without it, a late redelivery of the same event could skip the dedupe check and create a second vendor record in the AS.

  <br />

  <br />

  Once both windows have passed, it is safe to delete this data. Deleting it does not affect the original webhook events or the vendor record in the AS. This data exists only for the integration's own tracking.
</RememberCallout>

## Responsibility Model

### Pleo Responsibilities

| Area           | Responsibility                                                              |
| -------------- | --------------------------------------------------------------------------- |
| Vendor Capture | Let the bookkeeper enter the new vendor's details in the Web App            |
| Draft Storage  | Store the vendor in `DRAFT` state until activated                           |
| Notification   | Emit the `v1.vendor.created` webhook event when a `DRAFT` vendor is created |

### Integrator Responsibilities

| Area       | Responsibility                                                                 |
| ---------- | ------------------------------------------------------------------------------ |
| Detection  | Detect new `DRAFT` vendors, via webhook or scheduled polling                   |
| Creation   | Create the corresponding vendor record in the Accounting System                |
| Activation | Report the `code` and `externalId` back to Pleo so the vendor becomes `ACTIVE` |

## Operational Model

### Creation Direction

Vendor Creation flows in the opposite direction to Vendor Sync:

<VendorCreationDirectionDiagram />

* **Pleo** is the source of truth for the vendor's initial details
* The integration creates the corresponding record in the Accounting System
* The **Accounting System** is the source of truth for the resulting `code`. The integration reports that `code` back to Pleo

### Operational Constraints

| Constraint             | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Detection Ownership    | The integration is responsible for detecting new `DRAFT` vendors, ideally via webhook                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| Single-Item Processing | Each `DRAFT` vendor is processed independently as its own event                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| Rate Limits            | The integration must respect two rate limits. The first is the target Accounting System's own API rate limit; it varies by AS, and it's the integration's own responsibility to determine it. The second is [Pleo's Vendors API rate limit](/docs/current/authentication/api-base-urls#rate-limits). Pleo's limit is **600 requests/minute** per credential, shared across Vendor Creation's draft-vendor polling and vendor activation *and* Vendor Sync's scheduled and ad hoc runs. Size this workflow's request volume against that combined budget, not in isolation. See [Backpressure and Throttling](/docs/current/authentication/api-base-urls#backpressure-and-throttling) for what to do on a `429`. |
| Required Scopes        | The integration's credential must include the Vendors API scopes needed to read `DRAFT` vendors and activate them (see [Vendors API Scopes](/reference/vendor/vendor-api-scopes)). If you use webhook detection, the credential also needs webhook subscription permissions. A missing scope causes the specific request to fail with `403`, not `401`; that's distinct from the expired or revoked credential case below. Request the required scope during [OAuth client registration](/docs/current/how-tos/oauth/how-to-register-an-oauth-client#technical-information) or when creating an API Key.                                                                                                        |
| Credential Failures    | If Vendor Creation hits a `401` or `403` because of an expired, revoked, or rotated credential, stop processing. Don't continue with partial or unauthenticated calls. Resume once the credentials are restored. See [How to Handle Token Expiry or Revocation](/docs/current/how-tos/oauth/how-to-handle-token-expiry-or-revocation).                                                                                                                                                                                                                                                                                                                                                                          |
| Forward Compatibility  | Vendors API responses may gain new draft-vendor fields, new `state` values, and extra pagination metadata over time. Build the integration to ignore fields and values it doesn't recognise, instead of failing on them. Treat this suite's documented field list, and the `DRAFT`/`ACTIVE` states covered above, as the current set, not a permanent one. Vendor Sync separately handles the `ARCHIVED` state.                                                                                                                                                                                                                                                                                                 |
| Concurrency / Locking  | Vendor Creation shares a per-company concurrency lock with Vendor Sync. This stops the two workflows from writing to the same company's vendors at the same time. Hold the lock for the full lifetime of a pending activation (from AS creation succeeding to activation succeeding), not just for the Step 1/Step 2 calls. Otherwise, Vendor Sync could later create a duplicate for an AS vendor that is still `DRAFT` in Pleo. See [Create and Activate: Failure Handling](/docs/current/integration-design/exports/vendors/integration-design-vendor-creation-create-and-activate#failure-handling) for the full detail.                                                                                    |

For webhook and polling schedule details, see [Vendor Creation Workflow Guide](/docs/current/guides/vendor-creation-workflow-guide). It includes the recommended 5-minute polling interval for when you don't use webhooks.

***

## Related Reading

* [Supplier vs Vendor](/docs/current/platform/exports/vendors/supplier-vs-vendor-overview)
* [Integration Design: Vendor Creation Overview](/docs/current/integration-design/exports/vendors/integration-design-vendor-creation-overview)
* [Vendor Creation Workflow Guide](/docs/current/guides/vendor-creation-workflow-guide)
* [Platform Capabilities: Vendor Sync Overview](/docs/current/platform/accounting-integrations/imports/vendors/vendor-sync-overview)
* [How to Activate Vendor Tagging](/docs/current/how-tos/accounting-integrations/how-to-enable-vendor-based-bookkeeping#1-activate-vendor-tagging) (or [chat with support](https://help.pleo.io/en/support/solutions/articles/103000360463-how-to-activate-vendor-tagging-in-pleo) <Icon icon="arrow-up-right-from-square" size={14} />)

***
