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

# Update Expense Category

> Update an existing custom expense category

## Overview

The Update Expense Category endpoint allows you to modify existing custom expense categories. You can update the name, parent category, and visibility settings.

**Important Limitations**:

* You can only update **custom categories** that you created
* **System default categories** cannot be modified via the API
* Categories that belong to your firm only

## Path Parameters

<ParamField path="id" type="integer" required>
  The unique identifier of the expense category to update
</ParamField>

## Request Body

All parameters are optional - only include the fields you want to update.

<ParamField body="name" type="string">
  The new name for the expense category
</ParamField>

<ParamField body="parent_id" type="integer">
  New parent category ID. Use `0` to make it a root-level category, or specify an existing category ID to move it under that parent.
</ParamField>

<ParamField body="is_visible" type="boolean">
  Whether the category should be visible and available for use when creating expenses
</ParamField>

## Validation Rules

* **Name**: Must be non-empty if provided
* **Parent Category**: Must exist and belong to your firm (if specified)
* **Hierarchy**: Cannot create circular references or exceed 2-level depth
* **Self-Reference**: A category cannot be its own parent
* **System Categories**: Cannot update categories with `type: "system"`

## Hierarchy Constraints

* **Maximum Depth**: Categories can only be 2 levels deep (category → subcategory)
* **Parent Validation**: Parent must be a root-level category (parent\_id = 0)
* **Circular Reference**: Prevents categories from referencing themselves or creating loops

## Response

<ResponseField name="category" type="object">
  The updated expense category object

  <Expandable title="Category Object Properties">
    <ResponseField name="id" type="integer">
      Unique identifier for the category
    </ResponseField>

    <ResponseField name="name" type="string">
      Updated category name
    </ResponseField>

    <ResponseField name="parent_id" type="integer">
      Updated parent category ID (0 for root categories)
    </ResponseField>

    <ResponseField name="is_default" type="boolean">
      Always `false` for custom categories
    </ResponseField>

    <ResponseField name="is_visible" type="boolean">
      Updated visibility setting
    </ResponseField>

    <ResponseField name="type" type="string">
      Always `"custom"` for user-created categories
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="message" type="string">
  Success message confirming the category update
</ResponseField>

<RequestExample>
  ```bash Update Name Only theme={null}
  curl -X PUT "https://api.contazen.ro/v1/expense-categories/25" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Digital Marketing"
    }'
  ```

  ```bash Change Parent Category theme={null}
  curl -X PUT "https://api.contazen.ro/v1/expense-categories/25" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "parent_id": 5
    }'
  ```

  ```bash Multiple Fields Update theme={null}
  curl -X PUT "https://api.contazen.ro/v1/expense-categories/25" \
    -H "Authorization: Bearer YOUR_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "name": "Marketing & Advertising",
      "parent_id": 0,
      "is_visible": true
    }'
  ```

  ```javascript Update Name Only theme={null}
  const categoryId = 25;
  const updateData = {
    name: "Digital Marketing"
  };

  const response = await fetch(`https://api.contazen.ro/v1/expense-categories/${categoryId}`, {
    method: 'PUT',
    headers: {
      'Authorization': 'Bearer YOUR_API_KEY',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(updateData)
  });

  const data = await response.json();
  console.log('Updated category:', data.category);
  ```

  ```javascript Move to Different Parent theme={null}
  const categoryId = 25;
  const updateData = {
    parent_id: 5  // Move under category ID 5
  };

  const response = await fetch(`https://api.contazen.ro/v1/expense-categories/${categoryId}`, {
    method: 'PUT',
    headers: {
      'Authorization': 'Bearer YOUR_API_KEY',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(updateData)
  });

  const data = await response.json();
  console.log('Category moved to parent:', data.category.parent_id);
  ```

  ```javascript Complete Update theme={null}
  const categoryId = 25;
  const updateData = {
    name: "Marketing & Advertising",
    parent_id: 0,  // Make it root level
    is_visible: true
  };

  const response = await fetch(`https://api.contazen.ro/v1/expense-categories/${categoryId}`, {
    method: 'PUT',
    headers: {
      'Authorization': 'Bearer YOUR_API_KEY',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(updateData)
  });

  const data = await response.json();
  console.log('Updated category:', data.category);
  ```

  ```php Update Name Only theme={null}
  $categoryId = 25;
  $updateData = [
      'name' => 'Digital Marketing'
  ];

  $curl = curl_init();

  curl_setopt_array($curl, [
      CURLOPT_URL => "https://api.contazen.ro/v1/expense-categories/{$categoryId}",
      CURLOPT_RETURNTRANSFER => true,
      CURLOPT_CUSTOMREQUEST => 'PUT',
      CURLOPT_POSTFIELDS => json_encode($updateData),
      CURLOPT_HTTPHEADER => [
          'Authorization: Bearer YOUR_API_KEY',
          'Content-Type: application/json'
      ]
  ]);

  $response = curl_exec($curl);
  $data = json_decode($response, true);
  curl_close($curl);

  echo 'Updated category: ' . $data['category']['name'];
  ```

  ```php Change Parent and Visibility theme={null}
  $categoryId = 25;
  $updateData = [
      'parent_id' => 5,
      'is_visible' => false
  ];

  $curl = curl_init();

  curl_setopt_array($curl, [
      CURLOPT_URL => "https://api.contazen.ro/v1/expense-categories/{$categoryId}",
      CURLOPT_RETURNTRANSFER => true,
      CURLOPT_CUSTOMREQUEST => 'PUT',
      CURLOPT_POSTFIELDS => json_encode($updateData),
      CURLOPT_HTTPHEADER => [
          'Authorization: Bearer YOUR_API_KEY',
          'Content-Type: application/json'
      ]
  ]);

  $response = curl_exec($curl);
  $data = json_decode($response, true);
  curl_close($curl);

  echo 'Category moved to parent ID: ' . $data['category']['parent_id'];
  ```
</RequestExample>

<ResponseExample>
  ```json 200 - Success theme={null}
  {
    "success": true,
    "category": {
      "id": 25,
      "name": "Digital Marketing",
      "parent_id": 0,
      "is_default": false,
      "is_visible": true,
      "type": "custom"
    },
    "message": "Category updated successfully"
  }
  ```

  ```json 400 - Missing ID theme={null}
  {
    "success": false,
    "error": {
      "message": "Category ID is required",
      "type": "invalid_request_error",
      "code": "parameter_missing",
      "param": "id"
    }
  }
  ```

  ```json 404 - Not Found theme={null}
  {
    "success": false,
    "error": {
      "message": "Category not found",
      "type": "invalid_request_error",
      "code": "resource_missing",
      "param": "id"
    }
  }
  ```

  ```json 403 - System Category theme={null}
  {
    "success": false,
    "error": {
      "message": "Cannot update system category",
      "type": "invalid_request_error",
      "code": "system_category"
    }
  }
  ```

  ```json 400 - Self Reference theme={null}
  {
    "success": false,
    "error": {
      "message": "Category cannot be its own parent",
      "type": "invalid_request_error",
      "code": "invalid_parent",
      "param": "parent_id"
    }
  }
  ```

  ```json 400 - Invalid Parent theme={null}
  {
    "success": false,
    "error": {
      "message": "Parent category not found",
      "type": "invalid_request_error",
      "code": "invalid_parent",
      "param": "parent_id"
    }
  }
  ```

  ```json 400 - Invalid Hierarchy theme={null}
  {
    "success": false,
    "error": {
      "message": "Cannot create subcategory under another subcategory",
      "type": "invalid_request_error",
      "code": "invalid_parent_level",
      "param": "parent_id"
    }
  }
  ```

  ```json 400 - Circular Reference theme={null}
  {
    "success": false,
    "error": {
      "message": "Circular reference detected",
      "type": "invalid_request_error",
      "code": "circular_reference",
      "param": "parent_id"
    }
  }
  ```

  ```json 403 - Permission Denied theme={null}
  {
    "success": false,
    "error": {
      "message": "You don't have permission to update categories",
      "type": "invalid_request_error",
      "code": "permission_denied"
    }
  }
  ```
</ResponseExample>


## OpenAPI

````yaml PUT /expense-categories/{id}
openapi: 3.1.0
info:
  title: Contazen API
  version: 1.2.0
  description: >
    Build powerful integrations with the Contazen invoicing platform. The
    Contazen API is organized around REST, 

    has predictable resource-oriented URLs, accepts JSON request bodies, returns
    JSON-encoded responses, 

    and uses standard HTTP response codes, authentication, and verbs.


    ## Authentication

    The API uses Bearer token authentication. Include your API key in the
    Authorization header.


    ### Getting your API Key

    1. Log in to your Contazen account

    2. Navigate to Settings > API

    3. Generate or copy your API key (starts with `sk_live_` for production or
    `sk_test_` for testing)


    ### Using the API Key

    Include your API key in the Authorization header:

    ```

    Authorization: Bearer sk_live_YOUR_API_KEY

    ```


    ### Example Request with cURL

    ```bash

    curl --request GET \
      --url https://api.contazen.ro/v1/clients \
      --header 'Authorization: Bearer sk_live_YOUR_API_KEY' \
      --header 'Accept: application/json'
    ```


    ## Rate Limiting

    - 1000 requests per hour per API key

    - 100 create operations per minute per API key


    Rate limit information is included in response headers:

    - `X-RateLimit-Limit`: Maximum requests allowed

    - `X-RateLimit-Remaining`: Requests remaining

    - `X-RateLimit-Reset`: Reset time (Unix timestamp)


    ## Pagination

    All list endpoints return paginated results with the following format:

    ```json

    {
      "success": true,
      "data": {
        "object": "list",
        "data": [...],
        "has_more": true,
        "total": 245,
        "page": 1,
        "per_page": 50,
        "total_pages": 5
      },
      "meta": {
        "version": "v1",
        "request_id": "req_1a2b3c4d",
        "response_time": "23.45ms"
      }
    }

    ```


    ## Multi-Work-Point Access

    API keys belong to a specific work point but can access data from all work
    points

    within the same parent company.


    ## Error Handling

    The API uses conventional HTTP response codes to indicate success or
    failure. 

    In general: 2xx codes indicate success, 4xx codes indicate an error due to
    the 

    information provided, and 5xx codes indicate an error with Contazen's
    servers.


    ## Expanding Nested Objects

    Many endpoints support the `expand` parameter to include related objects in
    the response.

    This follows the Stripe API pattern. For example:

    - `expand[]=lines` - Include invoice line items

    - `expand[]=payments` - Include payment records

    - `expand[]=client` - Include full client object


    ## Localization

    The API supports multiple languages through:

    - `locale` query parameter (en, ro)

    - `Accept-Language` header

    - Default: English
  contact:
    name: Contazen Support
    email: support@contazen.ro
    url: https://contazen.ro
  license:
    name: Proprietary
    url: https://www.contazen.ro/termeni-si-conditii-de-utilizare/
servers:
  - url: https://api.contazen.ro/v1
    description: Production API server
security:
  - bearerAuth: []
tags:
  - name: Authentication
    description: API authentication and test endpoints
  - name: Clients
    description: Manage your customers (B2B and B2C)
  - name: Invoices
    description: Create and manage invoices, proformas, and receipts
  - name: Products
    description: Manage your product and service catalog
  - name: Expenses
    description: Track and manage business expenses
  - name: Expense Categories
    description: Organize expenses with categories
  - name: Suppliers
    description: Manage expense suppliers and vendors
  - name: Settings
    description: API settings and configuration
  - name: Payments
    description: Payments received against invoices
  - name: Receipts
    description: Cash receipts (chitanțe) — standalone or paired with an invoice
  - name: Company Lookup
    description: Romanian company lookup (ANAF / VIES)
  - name: Invoice Series
    description: Manage invoice numbering series
  - name: Bank Accounts
    description: Manage IBAN bank accounts
  - name: E-Factura
    description: Romanian e-invoicing status and configuration
  - name: Supplier Bills
    description: Supplier invoices imported from the ANAF SPV inbox
  - name: VAT Rates
    description: Firm-scoped custom VAT rates on top of the Romanian catalog
  - name: Currencies
    description: Currencies enabled for the firm's bill templates
  - name: Languages
    description: Languages enabled for the firm's bill templates
  - name: Conta
    description: |
      Public ANAF data for the firm's CUI: fiscal profile (TVA scope, RTVAI,
      split TVA, status, e-Factura registration, CAEN) and annual balance
      sheets (cifra de afaceri, profit, capitaluri, datorii, salariați).

      Powered by the public ANAF webservices (no OAuth required for these
      endpoints). The data is cached locally and refreshed on demand via
      the `/sync` actions.
paths:
  /expense-categories/{id}:
    put:
      tags:
        - Expense Categories
      summary: Update expense category
      description: Update a custom expense category (cannot modify system categories)
      operationId: updateExpenseCategory
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: integer
          description: Category ID
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ExpenseCategoryUpdateRequest'
      responses:
        '200':
          description: Category updated
          content:
            application/json:
              schema:
                type: object
                properties:
                  success:
                    type: boolean
                  data:
                    $ref: '#/components/schemas/ExpenseCategory'
                  meta:
                    $ref: '#/components/schemas/ResponseMeta'
        '401':
          $ref: '#/components/responses/UnauthorizedError'
        '403':
          description: Cannot modify system categories
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '404':
          $ref: '#/components/responses/NotFoundError'
components:
  schemas:
    ExpenseCategoryUpdateRequest:
      type: object
      properties:
        name:
          type: string
        parent_id:
          type: integer
        is_visible:
          type: integer
          enum:
            - 0
            - 1
    ExpenseCategory:
      type: object
      required:
        - id
        - name
        - type
      properties:
        id:
          type: integer
        name:
          type: string
        parent_id:
          type: integer
          nullable: true
        is_default:
          type: boolean
          description: Whether this is a system default category
        is_visible:
          type: boolean
        type:
          type: string
          enum:
            - system
            - custom
        expense_count:
          type: integer
          description: Number of expenses in this category
        children:
          type: array
          items:
            $ref: '#/components/schemas/ExpenseCategory'
    ResponseMeta:
      type: object
      properties:
        version:
          type: string
          default: v1
        request_id:
          type: string
          format: uuid
          description: Unique request identifier for debugging
        response_time:
          type: string
          example: 23.45ms
    ErrorResponse:
      type: object
      required:
        - success
        - error
      properties:
        success:
          type: boolean
          default: false
        error:
          type: object
          required:
            - message
            - type
            - code
          properties:
            message:
              type: string
              description: Human-readable error message
            type:
              type: string
              enum:
                - api_error
                - authentication_error
                - invalid_request_error
                - rate_limit_error
                - permission_error
                - validation_error
            code:
              type: string
              description: Machine-readable error code
            param:
              type: string
              description: The parameter that caused the error
            doc_url:
              type: string
              format: uri
              description: URL to relevant documentation
        meta:
          $ref: '#/components/schemas/ResponseMeta'
  responses:
    UnauthorizedError:
      description: API key is missing or invalid
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
    NotFoundError:
      description: The specified resource was not found
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      description: Use your API key (sk_live_xxx or sk_test_xxx)

````