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

# Upsert form submission

> Create or update a form submission. You have flexible options for identifying forms, sites, and form elements:

**Form Identification** (choose one):
- `formId`: Direct form ID (e.g., "form-abc123")
- `formMetadataKey` + `formMetadataValue`: Lookup by metadata (e.g., "erpFormId" = "form123")

**Site Identification** (choose one):
- `siteId`: Direct site ID (e.g., "site-xyz789")
- `siteMetadataKey` + `siteMetadataValue`: Lookup by metadata (e.g., "erpSiteId" = "123")

**Form Element Identification** (choose one per element):
- `elementId`: Direct element ID (e.g., "formel-def456")
- `elementKey`: Element key within the form (e.g., "energy_consumption")
- `elementMetadataKey` + `elementMetadataValue`: Lookup by metadata (e.g., "extId" = "ext01")

**TIMESTAMP elements**:
- Provide `values[].timestamp` (ISO 8601). The server requires it for TIMESTAMP elements, derives `periodUnit` from the UTC month, and validates that the timestamp year matches the submission year.
- Upsert semantics: entries are de-duplicated by (formSubmissionId, formElementId, recordedAt). Sending the same timestamp updates the existing entry; otherwise, a new entry is inserted.
- To delete a TIMESTAMP entry, send `value: null` with the same `timestamp`.

Example request (TIMESTAMP):
```json
{
  "formId": "form-123",
  "siteId": "site-1",
  "year": 2024,
  "values": [
    { "elementId": "el-ts", "value": 42, "timestamp": "2024-03-15T10:30:00Z", "periodUnit": 3 }
  ]
}
```

Example response (trimmed):
```json
{
  "id": "fs-1",
  "formSiteId": "fs-site-1",
  "values": [
    { "elementId": "el-ts", "periodUnit": 3, "value": 42, "recordedAt": "2024-03-15T10:30:00.000Z" }
  ]
}
```

This flexibility allows integration with external systems using their own IDs via metadata, while also supporting direct internal ID references.



## OpenAPI

````yaml https://app.azalt.co/api/v1/openapi.json post /form-submissions
openapi: 3.0.3
info:
  title: Erguvan Public API
  version: 1.0.0
servers:
  - url: https://app.azalt.co/api/v1
security: []
paths:
  /form-submissions:
    post:
      tags:
        - FormSubmission
      summary: Upsert form submission
      description: >-
        Create or update a form submission. You have flexible options for
        identifying forms, sites, and form elements:


        **Form Identification** (choose one):

        - `formId`: Direct form ID (e.g., "form-abc123")

        - `formMetadataKey` + `formMetadataValue`: Lookup by metadata (e.g.,
        "erpFormId" = "form123")


        **Site Identification** (choose one):

        - `siteId`: Direct site ID (e.g., "site-xyz789")

        - `siteMetadataKey` + `siteMetadataValue`: Lookup by metadata (e.g.,
        "erpSiteId" = "123")


        **Form Element Identification** (choose one per element):

        - `elementId`: Direct element ID (e.g., "formel-def456")

        - `elementKey`: Element key within the form (e.g., "energy_consumption")

        - `elementMetadataKey` + `elementMetadataValue`: Lookup by metadata
        (e.g., "extId" = "ext01")


        **TIMESTAMP elements**:

        - Provide `values[].timestamp` (ISO 8601). The server requires it for
        TIMESTAMP elements, derives `periodUnit` from the UTC month, and
        validates that the timestamp year matches the submission year.

        - Upsert semantics: entries are de-duplicated by (formSubmissionId,
        formElementId, recordedAt). Sending the same timestamp updates the
        existing entry; otherwise, a new entry is inserted.

        - To delete a TIMESTAMP entry, send `value: null` with the same
        `timestamp`.


        Example request (TIMESTAMP):

        ```json

        {
          "formId": "form-123",
          "siteId": "site-1",
          "year": 2024,
          "values": [
            { "elementId": "el-ts", "value": 42, "timestamp": "2024-03-15T10:30:00Z", "periodUnit": 3 }
          ]
        }

        ```


        Example response (trimmed):

        ```json

        {
          "id": "fs-1",
          "formSiteId": "fs-site-1",
          "values": [
            { "elementId": "el-ts", "periodUnit": 3, "value": 42, "recordedAt": "2024-03-15T10:30:00.000Z" }
          ]
        }

        ```


        This flexibility allows integration with external systems using their
        own IDs via metadata, while also supporting direct internal ID
        references.
      operationId: upsertFormSubmission
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                formId:
                  type: string
                formMetadataKey:
                  type: string
                formMetadataValue:
                  type: string
                siteId:
                  type: string
                siteMetadataKey:
                  type: string
                siteMetadataValue:
                  type: string
                year:
                  type: number
                values:
                  type: array
                  items:
                    type: object
                    properties:
                      elementId:
                        type: string
                      elementKey:
                        type: string
                      elementMetadataKey:
                        type: string
                      elementMetadataValue:
                        type: string
                      value: {}
                      periodUnit:
                        type: number
                      selectedUnitId:
                        type: string
                      timestamp:
                        anyOf:
                          - type: string
                          - type: string
                    required:
                      - periodUnit
              required:
                - year
                - values
      responses:
        '200':
          description: Successful response
          content:
            application/json:
              schema:
                type: object
                properties:
                  id:
                    type: string
                  createdAt:
                    type: string
                  updatedAt:
                    type: string
                  formSiteId:
                    type: string
                  userId:
                    type: string
                    nullable: true
                  organizationId:
                    type: string
                    nullable: true
                  values:
                    type: array
                    items:
                      type: object
                      properties:
                        formElementSubmissionId:
                          type: string
                          nullable: true
                        elementId:
                          type: string
                        value: {}
                        originalValue:
                          nullable: true
                        periodUnit:
                          type: number
                        selectedUnitId:
                          type: string
                          nullable: true
                        status:
                          type: string
                          nullable: true
                          enum:
                            - COMPLETED
                            - APPROVED
                            - REJECTED
                            - null
                        userId:
                          type: string
                          nullable: true
                        userName:
                          type: string
                          nullable: true
                        userEmail:
                          type: string
                          nullable: true
                        userImage:
                          type: string
                          nullable: true
                        recordedAt:
                          type: string
                          nullable: true
                      required:
                        - formElementSubmissionId
                        - elementId
                        - periodUnit
                        - status
                  deletedAt:
                    type: string
                    nullable: true
                required:
                  - id
                  - createdAt
                  - updatedAt
                  - formSiteId
                  - userId
                  - organizationId
                  - values
                  - deletedAt
        '400':
          description: Invalid input data
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/error.BAD_REQUEST'
        '401':
          description: Authorization not provided
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/error.UNAUTHORIZED'
        '403':
          description: Insufficient access
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/error.FORBIDDEN'
        '500':
          description: Internal server error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/error.INTERNAL_SERVER_ERROR'
      security:
        - Authorization: []
components:
  schemas:
    error.BAD_REQUEST:
      type: object
      properties:
        message:
          type: string
          description: The error message
          example: Invalid input data
        code:
          type: string
          description: The error code
          example: BAD_REQUEST
        issues:
          type: array
          items:
            type: object
            properties:
              message:
                type: string
            required:
              - message
          description: An array of issues that were responsible for the error
          example: []
      required:
        - message
        - code
      title: Invalid input data error (400)
      description: The error information
      example:
        code: BAD_REQUEST
        message: Invalid input data
        issues: []
    error.UNAUTHORIZED:
      type: object
      properties:
        message:
          type: string
          description: The error message
          example: Authorization not provided
        code:
          type: string
          description: The error code
          example: UNAUTHORIZED
        issues:
          type: array
          items:
            type: object
            properties:
              message:
                type: string
            required:
              - message
          description: An array of issues that were responsible for the error
          example: []
      required:
        - message
        - code
      title: Authorization not provided error (401)
      description: The error information
      example:
        code: UNAUTHORIZED
        message: Authorization not provided
        issues: []
    error.FORBIDDEN:
      type: object
      properties:
        message:
          type: string
          description: The error message
          example: Insufficient access
        code:
          type: string
          description: The error code
          example: FORBIDDEN
        issues:
          type: array
          items:
            type: object
            properties:
              message:
                type: string
            required:
              - message
          description: An array of issues that were responsible for the error
          example: []
      required:
        - message
        - code
      title: Insufficient access error (403)
      description: The error information
      example:
        code: FORBIDDEN
        message: Insufficient access
        issues: []
    error.INTERNAL_SERVER_ERROR:
      type: object
      properties:
        message:
          type: string
          description: The error message
          example: Internal server error
        code:
          type: string
          description: The error code
          example: INTERNAL_SERVER_ERROR
        issues:
          type: array
          items:
            type: object
            properties:
              message:
                type: string
            required:
              - message
          description: An array of issues that were responsible for the error
          example: []
      required:
        - message
        - code
      title: Internal server error error (500)
      description: The error information
      example:
        code: INTERNAL_SERVER_ERROR
        message: Internal server error
        issues: []
  securitySchemes:
    Authorization:
      type: http
      scheme: bearer

````