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

# Create Employee Payroll Run

> Creates a Employee Payroll Run object with the given values.

export const IntegrationSupport = ({category = "HRIS", model, direction = "read"}) => {
  const SHEET_BASE = "https://docs.google.com/spreadsheets/d/e/2PACX-1vTx0G0yItXlZKO4Zep8wstZuvvO7bgOxFXBVK_1vvQnxpG8H2hP9n9M8kmZMfIoo7ZO4e7_utrz3_XB/pub";
  const SHEET_GIDS = {
    HRIS: {
      read: "0",
      write: "1626747715"
    },
    ATS: {
      read: "1962490874"
    },
    LMS: {
      read: "1106223511"
    }
  };
  const LOGO_ROW = "logo link";
  const HEADER_ROW = "model";
  const TYPE_ROW = "connection type";
  const parseCsv = text => {
    const rows = [];
    let row = [];
    let field = "";
    let quoted = false;
    for (let i = 0; i < text.length; i++) {
      const c = text[i];
      if (quoted) {
        if (c === '"') {
          if (text[i + 1] === '"') {
            field += '"';
            i++;
          } else {
            quoted = false;
          }
        } else {
          field += c;
        }
      } else if (c === '"') {
        quoted = true;
      } else if (c === ",") {
        row.push(field);
        field = "";
      } else if (c === "\n" || c === "\r") {
        if (c === "\r" && text[i + 1] === "\n") i++;
        row.push(field);
        rows.push(row);
        row = [];
        field = "";
      } else {
        field += c;
      }
    }
    if (field !== "" || row.length) {
      row.push(field);
      rows.push(row);
    }
    return rows;
  };
  const cell = (row, i) => (row && row[i] || "").trim();
  const parseSheet = (csv, wanted) => {
    const rows = parseCsv(csv);
    let header = null;
    let labelCol = -1;
    rows.slice(0, 12).forEach(r => {
      if (header) return;
      for (let i = 0; i < Math.min(r.length, 6); i++) {
        if (cell(r, i).toLowerCase() === HEADER_ROW) {
          header = r;
          labelCol = i;
          return;
        }
      }
    });
    const find = label => rows.find(r => cell(r, labelCol).toLowerCase() === label);
    const types = header ? find(TYPE_ROW) : null;
    const logos = header ? find(LOGO_ROW) : null;
    if (!header || !types) return null;
    const providers = [];
    for (let i = labelCol + 1; i < header.length; i++) {
      const name = cell(header, i);
      const t = cell(types, i).toUpperCase();
      if (!name || t !== "A" && t !== "S") continue;
      providers.push({
        name,
        type: t === "S" ? "SFTP" : "API",
        col: i,
        uid: name + "#" + i,
        logo: cell(logos, i)
      });
    }
    const target = String(wanted || "").toLowerCase();
    const row = rows.find(r => cell(r, labelCol).toLowerCase() === target);
    if (!providers.length || !row) return null;
    const pool = providers;
    const supported = [];
    pool.forEach(p => {
      const v = cell(row, p.col).toUpperCase();
      if (v === "Y" || v === "B") supported.push({
        ...p
      });
    });
    if (!supported.length) return {
      none: true
    };
    return {
      total: pool.length,
      supported
    };
  };
  const CSS = `
  .bb-is {
    --bb-border: #e5e3df;
    --bb-border-strong: #d4d1cc;
    --bb-bg: #fcfcfb;
    --bb-bg-sub: #f5f4f3;
    --bb-text: #1c1b1a;
    --bb-text-dim: #6e6c68;
    --bb-accent: #f57e21;
    --bb-chip: #eceae7;
    font-size: 14px;
    color: var(--bb-text);
    border: 1px solid var(--bb-border);
    border-radius: 6px;
    background: var(--bb-bg);
    margin: 1.5rem 0;
    overflow: hidden;
  }
  html.dark .bb-is {
    --bb-border: #242424;
    --bb-border-strong: #333333;
    --bb-bg: #090909;
    --bb-bg-sub: #141414;
    --bb-text: #ededed;
    --bb-text-dim: #a1a1a1;
    --bb-chip: #1b1b1b;
  }

  .bb-is-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
    padding: 11px 14px;
    background: var(--bb-bg-sub);
    border-bottom: 1px solid var(--bb-border);
  }
  .bb-is-title {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-weight: 600;
    font-size: 16px;
  }
  /* Mintlify's <Icon> paints itself via a mask, so this makes it take the
     title's colour instead of staying its own. */
  .bb-is-title .icon { background-color: currentColor !important; }
  .bb-is-count {
    font-size: 12px;
    color: var(--bb-text-dim);
  }

  .bb-is-chips {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    padding: 12px 14px;
  }
  /* Collapsed by default: one row of chips, the next row fading out under a
     gradient so it reads as "there is more" rather than as a hard crop. The
     wrapper does the clipping so the chips keep their own padding. */
  .bb-is-chips-wrap { position: relative; }
  .bb-is-chips-wrap[data-open="false"] .bb-is-chips {
    max-height: 47px;
    overflow: hidden;
  }
  .bb-is-fade {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 28px;
    pointer-events: none;
    background: linear-gradient(to bottom, rgba(252, 252, 251, 0), var(--bb-bg));
  }
  html.dark .bb-is-fade {
    background: linear-gradient(to bottom, rgba(9, 9, 9, 0), var(--bb-bg));
  }
  .bb-is-toggle {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 4px 10px;
    border: 1px solid var(--bb-border-strong);
    border-radius: 999px;
    background: var(--bb-bg);
    color: var(--bb-text-dim);
    font-size: 12.5px;
    font-weight: 500;
    line-height: 1.5;
    cursor: pointer;
  }
  .bb-is-toggle:hover {
    color: var(--bb-text);
    border-color: var(--bb-text-dim);
  }
  .bb-is-headright {
    display: inline-flex;
    align-items: center;
    gap: 10px;
  }
  .bb-is-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 4px 9px;
    border: 1px solid var(--bb-border);
    border-radius: 999px;
    background: var(--bb-bg-sub);
    font-size: 12.5px;
    line-height: 1.4;
    white-space: nowrap;
  }
  .bb-is-logo {
    width: 15px;
    height: 15px;
    object-fit: contain;
    border-radius: 3px;
    flex: none;
  }
  .bb-is-badge {
    font-size: 9.5px;
    font-weight: 600;
    letter-spacing: 0.03em;
    padding: 1px 5px;
    border-radius: 3px;
    background: var(--bb-chip);
    color: var(--bb-text-dim);
  }
  /* Muted on purpose - the provider name should catch the eye in this list,
     not its connection-type badge. --bb-chip sat too close to the chip's own
     --bb-bg-sub to read as a separate shape, so the fill is a step further
     from it in each theme - darker in light mode, and lighter (not literally
     "darker") in dark mode, since going darker there would fade it into the
     near-black page instead of separating it. */
  .bb-is-badge[data-type="SFTP"] { color: var(--bb-text-dim); background: #ddd9d3; }
  html.dark .bb-is-badge[data-type="SFTP"] { background: #292929; }

  .bb-is-foot {
    padding: 9px 14px;
    border-top: 1px solid var(--bb-border);
    font-size: 14px;
    color: var(--bb-text-dim);
  }
  .bb-is-foot a { color: var(--bb-accent); text-decoration: none; }
  .bb-is-foot a:hover { text-decoration: underline; }

  .bb-is-none {
    padding: 16px 14px;
    font-size: 14px;
    line-height: 1.5;
    color: var(--bb-text-dim);
  }
  .bb-is-none a { color: var(--bb-accent); text-decoration: none; }
  .bb-is-none a:hover { text-decoration: underline; }

  .bb-is-loading {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 10px;
    padding: 28px 14px;
    font-size: 12.5px;
    color: var(--bb-text-dim);
  }
  .bb-is-spinner {
    width: 18px;
    height: 18px;
    border: 2px solid var(--bb-border);
    border-top-color: var(--bb-accent);
    border-radius: 50%;
    animation: bb-is-spin 0.7s linear infinite;
    flex: none;
  }
  @keyframes bb-is-spin {
    to { transform: rotate(360deg); }
  }
  /* Keep a motion cue for anyone who asked for less of it, just a calmer one. */
  @media (prefers-reduced-motion: reduce) {
    .bb-is-spinner { animation-duration: 2.4s; }
  }
  `;
  const [data, setData] = useState(null);
  const [open, setOpen] = useState(false);
  useEffect(() => {
    let live = true;
    const tabs = SHEET_GIDS[category] || ({});
    const gid = tabs[direction];
    if (!gid) return;
    const url = SHEET_BASE + "?gid=" + encodeURIComponent(gid) + "&single=true&output=csv&_=" + Date.now();
    fetch(url, {
      cache: "no-store"
    }).then(res => res.ok ? res.text() : Promise.reject(res.status)).then(text => {
      if (live) setData(parseSheet(text, model) || ({
        hide: true
      }));
    }).catch(() => {
      if (live) setData({
        hide: true
      });
    });
    return () => {
      live = false;
    };
  }, [category, model, direction]);
  if (data && data.hide) return null;
  const none = !!(data && data.none);
  const verb = direction === "write" ? "writing" : "reading";
  const mailto = "mailto:support@bindbee.dev?subject=" + encodeURIComponent("Integration request: " + verb + " " + model + " (" + category + ")");
  const collapsible = !!(data && !none && data.supported.length > 4);
  return <div className="bb-is not-prose">
      <style>{CSS}</style>
      <div className="bb-is-head">
        <span className="bb-is-title">
          <Icon icon="git-fork" size={15} />
          Supported integrations
        </span>
        {data && !none ? <span className="bb-is-headright">
            <span className="bb-is-count">
              {data.supported.length} integration{data.supported.length === 1 ? "" : "s"}
            </span>
            {collapsible ? <button type="button" className="bb-is-toggle" aria-expanded={open} onClick={() => setOpen(!open)}>
                {open ? "Show less" : "Show all"}
              </button> : null}
          </span> : null}
      </div>

      {!data ? <div className="bb-is-loading" role="status">
          <span className="bb-is-spinner" aria-hidden="true" />
          <span>Loading integration support…</span>
        </div> : none ? <div className="bb-is-none">
          No integration supports {verb} {model} yet.{" "}
          <a href={mailto}>Tell us which one you need</a>
        </div> : <div className="bb-is-chips-wrap" data-open={collapsible ? open : true}>
          <div className="bb-is-chips">
            {data.supported.map(p => <span className="bb-is-chip" key={p.uid}>
                {p.logo ? <img className="bb-is-logo" src={p.logo} alt="" loading="lazy" onError={e => {
    e.currentTarget.style.display = "none";
  }} /> : null}
                <span>{p.name}</span>
                {p.type === "SFTP" ? <span className="bb-is-badge" data-type="SFTP">
                    SFTP
                  </span> : null}
              </span>)}
          </div>
          {collapsible && !open ? <span className="bb-is-fade" /> : null}
        </div>}

      <div className="bb-is-foot">
        Coverage varies by integrations {" "}
        <a href="/get-started/model-availability">See the full availability matrix</a>
      </div>
    </div>;
};

<IntegrationSupport category="HRIS" model="Employee Payroll Run" direction="write" />

<Accordion title="Usecases">
  * [Create an Employee Payroll Run](/get-started/use-cases/write-payroll-deductions) - write earnings, deductions, and taxes for an employee into the customer's payroll system
</Accordion>

This endpoint creates an Employee Payroll Run in the connected HRIS via Bindbee's unified **Employee Payroll Runs** model.

## Provider-specific behavior

Different HRIS systems can have slightly different requirements and behaviors when creating payroll runs. Use the accordions below to see details for each provider.

<AccordionGroup>
  <Accordion title="Workday">
    <p>
      When the underlying HRIS is Workday, an `earning` can be created using a payload like:
    </p>

    ```json theme={null}
    {
      "employee": "019b2647-8e14-7fcf-aa9e-aeff6ab205b2",
      "start_date": "2025-12-01",
      "end_date": "2025-12-31",
      "earnings":
      [
        {
          "type": "Workday_Earning_Code",
          "amount": 200,
          "id": "W_1042S_2015"
        }
      ]
    }
    ```

    <p>
      Similarly, a `deduction` can be created using a payload like:
    </p>

    ```json theme={null}
    {
      "employee": "019b2647-8e14-7fcf-aa9e-aeff6ab205b2",
      "start_date": "2025-12-01",
      "end_date": "2025-12-31",
      "deductions":
      [
        {
          "type": "Workday_Deduction_Code",
          "amount": 200,
          "id": "W_1042S_2015"
        }
      ]
    }
    ```

    ### Important note

    * For Workday, you can add either `earnings` or `deductions` at a time - you cannot add both simultaneously.
    * You can only add 1 `earning` or `deduction` at a time, no more than that.
    * `payroll_run` field must be set to **null** for Workday integration.

    The possible types for `earnings` are:

    * **Workday\_Earning\_Code**
    * **WID**

    The possible types for `deductions` are:

    * **Workday\_Deduction\_Code**
    * **WID**
  </Accordion>

  <Accordion title="ADP">
    <p>
      When the underlying HRIS is <strong>ADP</strong>
      , an `employee_payroll_run` can be created using a payload like:
    </p>

    ```json theme={null}
    {
      "employee": "01929ee7-28b6-7abc-b4d3-e78610669c82",
      "earnings":
        [
          {
            "code": "BN",
            "amount": 0.01
          },
          {
            "code": "R",
            "amount": 0.04
          }
        ],
      "deductions":
        [
          {
            "code": "REI",
            "amount": 0.01
          },
          {
              "code": "KL1",
              "amount": 0.04
          }
        ],
      "taxes":
        [
          {
              "code": "9"
          }
        ],
      "payroll_file": "023321",
      "payroll_process_name": "Ded Ear writeback"
    }
    ```
  </Accordion>

  <Accordion title="UKG Ready">
    <p>
      When the underlying HRIS is <strong>UKG Ready</strong>
      , an `employee_payroll_run` can be created using a payload like:
    </p>

    ```json theme={null}
    {
      "employee": "0194cbef-a29f-7afe-8fb6-bcb706383346",
      "payroll_run": "0194cbef-a8e7-7784-ae98-bb6adaac63c6",
      "paystatement_type_id": 51743758,
      "deductions":
        [
          {
            "company_deduction": 5.5,
            "employee_deduction": 10.2,
            "name": "Student Loan"
          },
          {
            "company_deduction": 1.5,
            "name": "Dental PreTax"
          }
        ],
      "earnings":
        [
          {
            "name": "Overtime",
            "amount": 12.9
          }
        ]
    }
    ```
  </Accordion>

  <Accordion title="SAP SuccessFactors (SAP SF)">
    <p>
      When the underlying HRIS is **SAP SuccessFactors**, an `employee_payroll_run` can be created using a payload like:
    </p>

    ```json theme={null}
    {
      "employee": "019bdafb-0afb-782b-b03b-01c23271653d",
      "payroll_provider_id": "BindBee",
      "payroll_person_id": "310",
      "payroll_employment_id": "270",
      "start_date": "2026-01-26T08:00:00Z",
      "end_date": "2026-01-31T08:00:00Z",
      "deductions": [
        {
          "employee_deduction": 200,
          "name": "401K"
        }
      ],
      "earnings": [
        {
          "name": "GROSS",
          "amount": 2000
        }
      ],
      "taxes": [
        {
          "name": "Testing_Tax",
          "amount": 101
        }
      ]
    }
    ```

    ### Where to find these fields:

    * **payroll\_provider\_id**: Use the ID provided by the customer if it’s available. If not, any valid string can be used.
    * **payroll\_person\_id**: This value can be found in the employee’s `raw_data`.
    * **payroll\_employment\_id**: Source this either from the employee’s `raw_data` or from Binbee’s employment model (`remote_id`).
    * **Deductions**: When adding deductions, ensure the deduction codes are included in the name field.

    ### Important notes

    * All top-level fields are mandatory.
    * `earnings`, `deductions`, and `taxes` can be sent together.
    * All fields inside each array are compulsory.
  </Accordion>
</AccordionGroup>


## OpenAPI

````yaml POST /api/hris/v1/employee-payroll-runs
openapi: 3.1.0
info:
  title: Bindbee APIs
  version: 0.1.0
servers:
  - url: https://api.bindbee.dev
  - url: https://api-eu.bindbee.dev
security: []
paths:
  /api/hris/v1/employee-payroll-runs:
    post:
      tags:
        - Employee Payroll Runs
      summary: Create Employee Payroll Run
      description: Creates a Employee Payroll Run object with the given values.
      operationId: create_employee_payroll_run_api_hris_v1_employee_payroll_runs_post
      parameters:
        - name: x-idempotency-key
          in: header
          required: false
          schema:
            anyOf:
              - type: string
              - type: 'null'
            description: Key to guarantee idempotent write execution.
            title: X-Idempotency-Key
          description: Key to guarantee idempotent write execution.
      requestBody:
        content:
          application/json:
            schema:
              anyOf:
                - additionalProperties: true
                  type: object
                - type: 'null'
              title: Body
      responses:
        '201':
          description: Successful Response
          content:
            application/json:
              schema: {}
          headers:
            X-RateLimit-Limit:
              description: Maximum requests allowed in the current window.
              schema:
                type: integer
            X-RateLimit-Remaining:
              description: Requests remaining in the current window.
              schema:
                type: integer
            X-RateLimit-Reset:
              description: >-
                Unix timestamp (seconds since epoch) at which the current
                rate-limit window resets.
              schema:
                type: integer
        '401':
          description: Missing or invalid bearer authentication credentials.
          headers:
            WWW-Authenticate:
              description: Bearer authentication challenge.
              schema:
                type: string
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '403':
          description: >-
            The credentials are valid but do not permit access to this resource,
            e.g. a connector token used on a different API category or a model
            whose writes are disabled.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '409':
          description: >-
            The X-Idempotency-Key was used with a different body, or its first
            request is still running.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
        '429':
          description: Rate limit exceeded.
          headers:
            X-RateLimit-Limit:
              description: Maximum requests allowed in the current window.
              schema:
                type: integer
            X-RateLimit-Remaining:
              description: Requests remaining in the current window.
              schema:
                type: integer
            X-RateLimit-Reset:
              description: >-
                Unix timestamp (seconds since epoch) at which the current
                rate-limit window resets.
              schema:
                type: integer
            Retry-After:
              description: Seconds to wait before retrying the request.
              schema:
                type: integer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '501':
          description: The integration does not support this operation.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        default:
          description: The vendor's status and body, relayed as the vendor sent them.
      security:
        - HTTPBearer: []
          ConnectorToken: []
components:
  schemas:
    ErrorResponse:
      type: object
      required:
        - detail
      properties:
        detail:
          type: string
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
              - type: string
              - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
        input:
          title: Input
        ctx:
          type: object
          title: Context
      type: object
      required:
        - loc
        - msg
        - type
      title: ValidationError
  securitySchemes:
    HTTPBearer:
      type: http
      scheme: bearer
      x-default: <BINDBEE_API_KEY>
      description: >-
        Your Bindbee API key, sent as `Authorization: Bearer <BINDBEE_API_KEY>`.
        Required on every request.
    ConnectorToken:
      type: apiKey
      in: header
      name: X-Connector-Token
      x-default: <CONNECTOR_TOKEN>
      description: >-
        The connector token for one customer's connection. This is not your
        Bindbee API key. Required on requests that read or write a single
        customer's data.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.