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

# Create or update an item

> Creates an item, or updates the one with the same `externalId`. The item starts as `PENDING`: it is **not** registered with KRA until you call `POST /v1/items/{id}/register`. The call returns `201` for both a create and an update.

Required scope: `catalog:write`.



## OpenAPI

````yaml /openapi/etims.json post /v1/items
openapi: 3.1.0
info:
  title: Sync2Books Compliance API (eTIMS)
  version: 1.0.0
  description: >-
    Register items, issue sales and credit notes, record stock and read supplier
    invoices for a Kenyan business, with every fiscal document signed by KRA
    eTIMS.


    Authenticate with an API key from the compliance dashboard, sent in the
    `x-api-key` header or as a bearer token. A `cmp_sk_test_...` key acts on
    sandbox businesses and a `cmp_sk_live_...` key on production ones; both use
    the same base URL.


    New here? Start with the [eTIMS quickstart](/etims-quickstart). The
    conventions every endpoint shares are in [Authentication & API
    keys](/etims-authentication), [Errors & Troubleshooting](/etims-errors),
    [Idempotency](/etims-idempotency) and [Rate limits](/etims-rate-limits).


    This is a separate product from the [Accounting API](/api-reference), with
    its own keys.
servers:
  - url: https://sync2books-compliance-api.apps.kintro.money
    description: >-
      Sync2Books Compliance API. The key you use decides whether a call reaches
      the KRA sandbox or production.
security:
  - ApiKeyAuth: []
  - BearerAuth: []
tags:
  - name: Identity
    description: Check what a key can do.
  - name: Businesses
    description: The businesses and branches a key can act for.
  - name: Items
    description: >-
      Create items and register them with KRA. An item must be registered before
      it can be sold.
  - name: Stock
    description: Record stock movements and report them to KRA.
  - name: Sales
    description: Issue sales, read them back and download KRA-signed receipts.
  - name: Credit notes
    description: Reverse an accepted sale in full.
  - name: Purchases
    description: Supplier invoices KRA holds against the business.
  - name: Lookups
    description: KRA code lists and item classifications.
paths:
  /v1/items:
    post:
      tags:
        - Items
      summary: Create or update an item
      description: >-
        Creates an item, or updates the one with the same `externalId`. The item
        starts as `PENDING`: it is **not** registered with KRA until you call
        `POST /v1/items/{id}/register`. The call returns `201` for both a create
        and an update.


        Required scope: `catalog:write`.
      operationId: createOrUpdateItem
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateItemRequest'
            examples:
              goods:
                summary: A stocked product
                value:
                  externalId: SKU-1042
                  name: 500ml Cooking Oil
                  sku: COOK-OIL-500
                  taxCategory: VAT_STANDARD
                  productTypeCode: '2'
                  classificationCode: '50150000'
                  unitCode: U
                  packagingUnitCode: NT
                  unitPrice: 3500
      responses:
        '201':
          description: The item as stored.
          headers:
            X-RateLimit-Limit:
              $ref: '#/components/headers/RateLimitLimit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/RateLimitRemaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/RateLimitReset'
          content:
            application/json:
              schema:
                type: object
                required:
                  - data
                properties:
                  data:
                    type: object
                    properties:
                      item:
                        $ref: '#/components/schemas/Item'
              example:
                data:
                  item:
                    id: itm_example_1
                    externalId: SKU-1042
                    name: 500ml Cooking Oil
                    sku: COOK-OIL-500
                    taxCategory: VAT_STANDARD
                    taxTypeCode: B
                    productTypeCode: '2'
                    classificationCode: '50150000'
                    unitCode: U
                    packagingUnitCode: NT
                    unitPrice: 3500
                    stockTracked: true
                    registrationStatus: PENDING
                    needs:
                      productType: false
                      classificationMapping: false
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
components:
  schemas:
    CreateItemRequest:
      type: object
      required:
        - externalId
        - name
        - taxCategory
        - productTypeCode
      properties:
        externalId:
          type: string
          maxLength: 100
          description: Your own identifier. Sending it again updates the same item.
        name:
          type: string
          maxLength: 200
        sku:
          type: string
          maxLength: 100
        taxCategory:
          $ref: '#/components/schemas/TaxCategory'
        productTypeCode:
          type: string
          enum:
            - '1'
            - '2'
            - '3'
          description: >-
            A string, not a number. `1` raw material, `2` finished product, `3`
            service.
        classificationCode:
          type: string
          maxLength: 20
          description: >-
            The KRA item classification. Required before the item can be
            registered. Search with `GET /v1/lookups/classifications`.
        unitCode:
          type: string
          maxLength: 2
          description: >-
            KRA quantity unit code, for example `U`. See `GET
            /v1/lookups/codes?codeClass=10`.
        packagingUnitCode:
          type: string
          maxLength: 2
          description: >-
            KRA packaging unit code, for example `NT`. See `GET
            /v1/lookups/codes?codeClass=17`.
        unitPrice:
          type: number
          minimum: 0
          description: The default unit price.
        businessId:
          type: string
          description: >-
            The business to act for. Required for organisation-wide keys; omit
            it for a business-bound key. List the ids you can use with `GET
            /v1/businesses`.
    Item:
      type: object
      properties:
        id:
          type: string
          description: The item id. Use it on sale lines and stock movements.
        externalId:
          type: string
          description: Your own identifier for the item.
        name:
          type: string
        sku:
          type:
            - string
            - 'null'
        taxCategory:
          $ref: '#/components/schemas/TaxCategory'
        taxTypeCode:
          type:
            - string
            - 'null'
          description: The KRA tax type letter the category maps to.
        productTypeCode:
          type: string
          enum:
            - '1'
            - '2'
            - '3'
          description: '`1` raw material, `2` finished product, `3` service.'
        classificationCode:
          type:
            - string
            - 'null'
        unitCode:
          type:
            - string
            - 'null'
        packagingUnitCode:
          type:
            - string
            - 'null'
        unitPrice:
          type:
            - number
            - 'null'
          description: The default unit price.
        stockTracked:
          type: boolean
          description: Whether sales of this item deduct stock.
        registrationStatus:
          type: string
          enum:
            - PENDING
            - REGISTERED
            - FAILED
          description: Only a `REGISTERED` item can be sold.
        needs:
          type: object
          description: Why an item is not yet ready to register.
          properties:
            productType:
              type: boolean
              description: '`true` when `productTypeCode` is missing.'
            classificationMapping:
              type: boolean
              description: '`true` when `classificationCode` is missing.'
    TaxCategory:
      type: string
      enum:
        - VAT_STANDARD
        - VAT_ZERO
        - EXEMPT
        - OTHER
        - VAT_8
      description: >-
        `VAT_STANDARD` is KRA tax type B (16%), `VAT_ZERO` is C (0%), `EXEMPT`
        is A, `OTHER` is D and `VAT_8` is E (8%).
    Error:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
          additionalProperties: true
          properties:
            code:
              type: string
              description: >-
                A stable, machine-readable code. Branch on this. See [Errors &
                Troubleshooting](/etims-errors) for the full list.
              examples:
                - invalid_request
                - kra_rejected
                - already_credited
            message:
              type: string
              description: >-
                A human-readable explanation. The wording can change; do not
                parse it.
          description: >-
            Some errors carry extra fields next to `code` and `message`, for
            example `sale`, `item`, `errors`, `creditNoteId` or `branches`.
  headers:
    RateLimitLimit:
      description: Requests allowed per minute for your application.
      schema:
        type: integer
    RateLimitRemaining:
      description: Requests left in the current one-minute window.
      schema:
        type: integer
    RateLimitReset:
      description: >-
        Seconds until the current window resets. This is a number of seconds,
        not a Unix timestamp.
      schema:
        type: integer
  responses:
    BadRequest:
      description: >-
        The request is malformed. The message names the first field that is
        wrong.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            invalid_request:
              summary: invalid_request
              value:
                error:
                  code: invalid_request
                  message: lines[0].quantity must be greater than 0
    Unauthorized:
      description: >-
        The key is missing, malformed, revoked, expired or belongs to a
        suspended application. The response never says which.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            invalid_api_key:
              summary: invalid_api_key
              value:
                error:
                  code: invalid_api_key
                  message: Invalid API key
    Forbidden:
      description: >-
        The key is valid but may not do this: a missing scope, a business the
        key is not limited to, or a test key used against a production business
        (or the reverse).
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            missing_scope:
              summary: missing_scope
              value:
                error:
                  code: forbidden
                  message: This API key is missing the sales:write scope
            other_business:
              summary: other_business
              value:
                error:
                  code: forbidden
                  message: >-
                    This API key is limited to a single business and cannot act
                    for another one.
            environment_mismatch:
              summary: environment_mismatch
              value:
                error:
                  code: forbidden
                  message: >-
                    This is a test API key and Example Business Ltd is a
                    production business. Use the key for that environment.
    RateLimited:
      description: >-
        The application has used its requests for this minute. Wait
        `Retry-After` seconds.
      headers:
        X-RateLimit-Limit:
          $ref: '#/components/headers/RateLimitLimit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/RateLimitRemaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/RateLimitReset'
        Retry-After:
          description: Seconds to wait before trying again.
          schema:
            type: integer
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          examples:
            rate_limited:
              summary: rate_limited
              value:
                error:
                  code: rate_limited
                  message: >-
                    Rate limit of 120 requests per minute exceeded. Retry in
                    23s.
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: >-
        Your API key, for example `cmp_sk_test_xxxxxxxxxxxxxxxxxxxxxxxx`. Create
        one on the Integrations page of the compliance dashboard.
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: cmp_sk_...
      description: >-
        The same key, sent as `Authorization: Bearer <key>`. Use this if your
        HTTP client makes custom headers awkward.

````

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