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

# Update Questionnaire

> Update a questionnaire. Versioning depends on the `status` field you send and the current state of the version: (1) no `status` on a **published** version → the published version is updated in place (same `questionnaire_id`, same `version`); (2) no `status` on a **draft** → the draft is updated and immediately published; (3) `status: "published"` on a **published** version → a NEW published version is created under the same `questionnaire_group_id` (new `questionnaire_id`, `version` + 1) and returned, and any existing draft in the group is deleted; (4) `status: "draft"` keeps a draft as a draft (autosave) but is rejected with 400 on a published version (`"Cannot revert a published version to draft."`).

export const AgentPromptAccordion = ({prompt, title = "AI Agent Integration Prompt"}) => {
  const [copied, setCopied] = React.useState(false);
  const handleCopy = e => {
    e.stopPropagation();
    if (!prompt) return;
    navigator.clipboard.writeText(prompt.trim()).then(() => {
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    });
  };
  const agents = ["Claude Code", "Codex", "Cursor", "Devin", "Windsurf", "GitHub Copilot"];
  return <div className="didit-agent-card">
      {}
      <div className="didit-agent-titlebar">
        <div className="didit-agent-dots" aria-hidden="true">
          <span className="didit-agent-dot didit-agent-dot-red"></span>
          <span className="didit-agent-dot didit-agent-dot-yellow"></span>
          <span className="didit-agent-dot didit-agent-dot-green"></span>
        </div>
        <span className="didit-agent-filename">{title}</span>
        <button type="button" className={`didit-agent-copy ${copied ? "didit-agent-copy-copied" : ""}`} onClick={handleCopy} title="Copy prompt to clipboard" aria-label={copied ? "Copied!" : "Copy prompt to clipboard"}>
          {copied ? <>
              <svg width="13" height="13" viewBox="0 0 16 16" fill="none">
                <path d="M3 8.5l3.5 3.5L13 4" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
              </svg>
              <span>Copied</span>
            </> : <>
              <svg width="13" height="13" viewBox="0 0 16 16" fill="none">
                <rect x="5" y="5" width="9" height="9" rx="1.5" stroke="currentColor" strokeWidth="1.5" />
                <path d="M11 5V3.5A1.5 1.5 0 0 0 9.5 2h-6A1.5 1.5 0 0 0 2 3.5v6A1.5 1.5 0 0 0 3.5 11H5" stroke="currentColor" strokeWidth="1.5" />
              </svg>
              <span>Copy</span>
            </>}
        </button>
      </div>

      {}
      <pre className="didit-agent-body"><code>{prompt.trim()}</code></pre>

      {}
      <div className="didit-agent-footer">
        <span className="didit-agent-footer-label">Paste into</span>
        <div className="didit-agent-chips">
          {agents.map(name => <span key={name} className="didit-agent-chip">{name}</span>)}
        </div>
      </div>
    </div>;
};

<AgentPromptAccordion
  title="Update Questionnaire API Prompt"
  prompt={`Update a simple Didit questionnaire through the Management API.

Endpoint:
PATCH https://verification.didit.me/v3/questionnaires/{questionnaire_uuid}/

Authentication:
Use the x-api-key header with my Didit API key.

Requirements:
- Use PATCH and include only the fields I want to change.
- To rename the questionnaire, send title or description only.
- To change questions, send the full desired form_elements array. This replaces the existing question list.
- Do not send graph, branches, next, required_if, or conditional logic.
- Keep languages consistent with the questionnaire. Include en labels for every form element.
- Capture the questionnaire_id returned by the response because published updates may return a new version id.

Example metadata update body:
{
"title": "Updated Customer Onboarding"
}

Example full question replacement body:
{
"form_elements": [
{
  "id": "occupation",
  "element_type": "short_text",
  "label": { "en": "What is your occupation?" },
  "is_required": true
},
{
  "id": "source_of_funds",
  "element_type": "multiple_choice",
  "label": { "en": "Source of funds" },
  "options": [
    { "value": "employment", "label": { "en": "Employment" } },
    { "value": "business", "label": { "en": "Business" } }
  ]
}
]
}

Build the API call, handle validation errors, and return the latest questionnaire_id from the response.`}
/>

Use `PATCH` for partial updates. To rename a questionnaire, send only `title` or `description`.

```json theme={null}
{
  "title": "Updated Customer Onboarding"
}
```

To update questions, send the full desired `form_elements` array. This replaces the previous question list. Keep any existing questions you still want to show.

```json theme={null}
{
  "form_elements": [
    {
      "id": "occupation",
      "element_type": "short_text",
      "label": { "en": "What is your occupation?" },
      "is_required": true
    },
    {
      "id": "source_of_funds",
      "element_type": "multiple_choice",
      "label": { "en": "Source of funds" },
      "options": [
        { "value": "employment", "label": { "en": "Employment" } },
        { "value": "business", "label": { "en": "Business" } }
      ]
    }
  ]
}
```

The v3 API only supports simple linear questionnaires. It rejects `graph`, `branches`, `next`, and conditional rules.

The response includes `questionnaire_id`. When you update a `published` questionnaire, the API may create a new version. Capture the returned `questionnaire_id` and use it in workflows instead of assuming the id in the URL is still the latest version.


## OpenAPI

````yaml PATCH /v3/questionnaires/{questionnaire_uuid}/
openapi: 3.0.0
info:
  version: 3.0.0
  title: Didit Verification API
  description: Identity verification API. Authenticate with x-api-key header.
servers:
  - url: https://verification.didit.me
security: []
tags: []
paths:
  /v3/questionnaires/{questionnaire_uuid}/:
    patch:
      tags:
        - Questionnaires
      summary: Update questionnaire
      description: >-
        Update a questionnaire. Versioning depends on the `status` field you
        send and the current state of the version: (1) no `status` on a
        **published** version → the published version is updated in place (same
        `questionnaire_id`, same `version`); (2) no `status` on a **draft** →
        the draft is updated and immediately published; (3) `status:
        "published"` on a **published** version → a NEW published version is
        created under the same `questionnaire_group_id` (new `questionnaire_id`,
        `version` + 1) and returned, and any existing draft in the group is
        deleted; (4) `status: "draft"` keeps a draft as a draft (autosave) but
        is rejected with 400 on a published version (`"Cannot revert a published
        version to draft."`).
      operationId: update_questionnaire
      parameters:
        - name: questionnaire_uuid
          in: path
          required: true
          schema:
            type: string
            format: uuid
          description: Per-version UUID of the questionnaire to update.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SimpleQuestionnaireUpdateRequest'
            examples:
              Rename:
                summary: Rename a questionnaire
                value:
                  title: Updated Customer Onboarding
              Replace questions:
                summary: Replace the full linear question list
                value:
                  form_elements:
                    - id: occupation
                      element_type: short_text
                      label:
                        en: What is your occupation?
                      is_required: true
                    - id: source_of_funds
                      element_type: multiple_choice
                      label:
                        en: Source of funds
                      options:
                        - value: employment
                          label:
                            en: Employment
                        - value: business
                          label:
                            en: Business
      responses:
        '200':
          description: >-
            Questionnaire updated. The body is the latest state of the resulting
            version — the in-place updated version, or the newly created
            published version when you sent `status: "published"` on a published
            row (in that case `questionnaire_id` and `version` differ from the
            ones you patched). `questionnaire_id` in the response is the
            per-version UUID; use `questionnaire_group_id` if you need the
            stable group identifier.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/QuestionnaireDetail'
        '400':
          description: >-
            Validation error — unknown `element_type`, missing translation,
            unsupported branching/conditional fields, `languages` missing `en`,
            `default_language` not in `languages`, option without a label,
            sending a raw `graph`, or `status: "draft"` on a published version
            (`"Cannot revert a published version to draft."`). Response body is
            a DRF validation error keyed by field name.
        '403':
          description: API key missing or invalid.
          content:
            application/json:
              examples:
                Invalid token:
                  value:
                    detail: You do not have permission to perform this action.
        '404':
          description: >-
            No questionnaire with this `questionnaire_uuid` exists for the
            authenticated application.
        '415':
          description: 'Unsupported media type. Set `Content-Type: application/json`.'
        '429':
          description: Rate limit exceeded. Retry with exponential backoff.
        '500':
          description: Unexpected server error. Safe to retry.
      security:
        - ApiKeyAuth: []
      x-codeSamples:
        - lang: curl
          label: cURL
          source: >-
            curl -X PATCH
            'https://verification.didit.me/v3/questionnaires/11111111-2222-3333-4444-555555555555/'
            \
              -H 'x-api-key: YOUR_API_KEY' \
              -H 'Content-Type: application/json' \
              -d '{"title": "Updated Customer Onboarding"}'
        - lang: Python
          label: Python (requests)
          source: |-
            import requests

            resp = requests.patch(
                f"https://verification.didit.me/v3/questionnaires/{questionnaire_uuid}/",
                headers={"x-api-key": "YOUR_API_KEY"},
                json={"title": "Updated Customer Onboarding"},
                timeout=10,
            )
            resp.raise_for_status()
            print(resp.json()["questionnaire_id"], resp.json()["status"])
        - lang: JavaScript
          label: Node.js (fetch)
          source: >-
            const res = await
            fetch(`https://verification.didit.me/v3/questionnaires/${questionnaireUuid}/`,
            {
              method: "PATCH",
              headers: {
                'x-api-key': 'YOUR_API_KEY',
                "Content-Type": "application/json",
              },
              body: JSON.stringify({ title: "Updated Customer Onboarding" }),
            });

            if (!res.ok) throw new Error(`Didit ${res.status}`);

            const q = await res.json();

            console.log(q.questionnaire_id, q.status);
components:
  schemas:
    SimpleQuestionnaireUpdateRequest:
      type: object
      description: >-
        Update questionnaire metadata or replace the full linear `form_elements`
        list.
      properties:
        title:
          type: string
        description:
          type: string
          nullable: true
        languages:
          type: array
          items:
            type: string
        default_language:
          type: string
        is_active:
          type: boolean
        status:
          type: string
          enum:
            - draft
            - published
        form_elements:
          type: array
          minItems: 1
          description: If provided, this replaces the full linear question list.
          items:
            $ref: '#/components/schemas/SimpleQuestionnaireElement'
    QuestionnaireDetail:
      type: object
      description: >-
        Full questionnaire detail returned from GET, POST (create) and PATCH
        (update) endpoints.
      properties:
        questionnaire_id:
          type: string
          format: uuid
          description: >-
            Unique identifier of the questionnaire. Use this id when referencing
            the questionnaire from a workflow or in subsequent update/delete
            requests.
        title:
          type: string
        description:
          type: string
          nullable: true
        languages:
          type: array
          items:
            type: string
        default_language:
          type: string
        is_active:
          type: boolean
        is_simple_questionnaire:
          type: boolean
        graph:
          type: object
          description: Graph structure with start_node and nodes map.
        sections:
          type: array
          description: >-
            Questionnaire content grouped into sections (derived from the
            graph).
          items:
            type: object
        questionnaire_group_id:
          type: string
          format: uuid
          description: >-
            Stable identifier that groups all versions of the same
            questionnaire.
        version:
          type: integer
        status:
          type: string
          enum:
            - draft
            - published
        published_at:
          type: string
          format: date-time
          nullable: true
        is_editable:
          type: boolean
          description: >-
            True when the questionnaire can still be edited in place (draft
            versions).
    SimpleQuestionnaireElement:
      type: object
      required:
        - element_type
        - label
      properties:
        id:
          type: string
          description: >-
            Optional stable question id. If omitted, the API generates `q1`,
            `q2`, etc. Use lowercase letters, numbers, and underscores for
            readability.
          example: source_of_funds
        element_type:
          type: string
          description: >-
            Question type. The API accepts these lowercase values and their
            uppercase equivalents.
          enum:
            - short_text
            - long_text
            - dropdown
            - single_choice
            - multiple_choice
            - number
            - image
            - file_upload
            - time
            - email
            - address
            - phone
            - country
            - date_picker
            - consent
            - section_header
            - separator
            - heading
            - paragraph
        label:
          type: object
          description: >-
            Question label by language code, for example `{ "en": "Source of
            funds" }`.
          additionalProperties:
            type: string
        description:
          type: object
          nullable: true
          additionalProperties:
            type: string
        placeholder:
          type: object
          nullable: true
          additionalProperties:
            type: string
        is_required:
          type: boolean
          default: false
        options:
          type: array
          description: >-
            Use for `dropdown`, `single_choice`, and `multiple_choice`. If
            `value` is omitted, the API generates one from the option label.
          items:
            type: object
            required:
              - label
            properties:
              value:
                type: string
              label:
                type: object
                additionalProperties:
                  type: string
        max_files:
          type: integer
          minimum: 1
          maximum: 5
          description: Use with `file_upload` or `image`.
        validation:
          type: object
          nullable: true
          description: >-
            Optional format validation for `short_text`, `long_text`, or
            `number`. Enforced while the user fills the field (they cannot
            proceed until it passes) and re-checked by the API on submission.
          properties:
            pattern:
              type: string
              description: >-
                Regular expression (ECMA-262 subset) the value must fully match.
                For `short_text` and `long_text`. Do not add ^/$ anchors; the
                whole value is matched.
            min_length:
              type: integer
              minimum: 0
              description: Minimum number of characters. For `short_text` and `long_text`.
            max_length:
              type: integer
              minimum: 0
              description: Maximum number of characters. For `short_text` and `long_text`.
            min:
              type: number
              description: Minimum numeric value. For `number`.
            max:
              type: number
              description: Maximum numeric value. For `number`.
            error_message:
              type: object
              description: >-
                Translated message shown when validation fails, keyed by
                language code.
              additionalProperties:
                type: string
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key

````