> ## 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.

# Create Session Review

> Append a note to a User Verification (KYC) session's review / activity feed. **This endpoint only records an entry — it never changes the session.** The session's `status` is left untouched, no status-transition validation runs, and no webhook fires. To actually approve, decline, or re-open a session (which also writes a fully-attributed `STATUS_UPDATED` entry to this feed), use `PATCH /v3/session/{session_id}/update-status/` instead.

Both body fields are optional and an empty JSON object is accepted. `new_status` is stored purely as a label on the entry and accepts any of the ten session status values (case-sensitive). `@email` mentions inside `comment` are stored as plain text only — they trigger no notifications on this endpoint, and the entry's `mentioned_emails` stays empty.

Note on attribution: entries created here carry no actor information. In the feed they show `actor_display: "Unknown"`, `actor_type: "CONSOLE_USER"`, and `activity_type: "STATUS_UPDATED"` (storage defaults), and the `201` body's `user` field is `"Unknown"`.

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="Create Session Review Prompt"
  prompt={`Goal: Push a manual decision (Approve / Decline / mark In Review) onto a Didit verification session and write an immutable audit-trail row. Programmatic equivalent of the Console manual-review buttons.

Endpoint: POST https://verification.didit.me/v3/sessions/{session_id}/reviews/
Auth header: x-api-key: YOUR_API_KEY
Content-Type: application/json

Path param:
- session_id (UUID, REQUIRED) — verification session UUID. Must belong to the same application as the API key (cross-application writes return 404).

Request body:
- new_status (string, REQUIRED) — one of "Approved" | "Declined" | "In Review". Workflow-internal statuses ("In Progress", "Awaiting User", "Resubmitted", "Expired", "Kyc Expired", "Abandoned", "Not Started") are NOT accepted (400).
- comment (string, optional, max 10,000 chars) — reviewer rationale. Supports @email mentions which trigger Console notifications.

curl:
curl -s -X POST 'https://verification.didit.me/v3/sessions/8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d/reviews/' \\
-H "x-api-key: $DIDIT_API_KEY" \\
-H "Content-Type: application/json" \\
-d '{"new_status": "Approved", "comment": "POA cross-checked against utility bill; matches address on file."}'

Response 201 (compact ReviewSerializer shape — different from the GET response):
{
"user": "API Client",                       // actor display label
"new_status": "Approved",
"comment": "POA cross-checked...",
"created_at": "2026-04-21T16:42:50.445Z"
}
To fetch the full activity row (uuid, activity_type, previous_status, ...) call GET /v3/sessions/{session_id}/reviews/ afterwards. See /reference/data-models for the full ReviewItem shape.

Side effects:
1. Inserts a Review row with activity_type=STATUS_UPDATED, actor_type=API_KEY.
2. Updates Session.status to new_status if it differs from the current status.
3. Fires the status.updated webhook on configured destinations (see /integration/webhooks).
4. Notifies previous reviewers and @email-mentioned Console users.
NOT idempotent — every call writes a new audit row. Re-posting the same new_status when it equals the current status is a no-op for Session.status but still writes an audit entry.

Failure modes (envelope {"detail": "..."} unless noted):
- 400 — { "new_status": ["\\"Expired\\" is not a valid choice."] } (or missing new_status)
- 401 — missing/invalid \`x-api-key\` header.
- 403 — API key valid but wrong app or revoked.
- 404 — session UUID not found / soft-deleted / wrong app.
- 429 — rate-limited.

For end-to-end Didit integration, paste in the full prompt at /integration/integration-prompt.`}
/>


## OpenAPI

````yaml POST /v3/sessions/{session_id}/reviews/
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/sessions/{session_id}/reviews/:
    post:
      tags:
        - Sessions
      summary: Add a review note to a session's audit trail
      description: >-
        Append a note to a User Verification (KYC) session's review / activity
        feed. **This endpoint only records an entry — it never changes the
        session.** The session's `status` is left untouched, no
        status-transition validation runs, and no webhook fires. To actually
        approve, decline, or re-open a session (which also writes a
        fully-attributed `STATUS_UPDATED` entry to this feed), use `PATCH
        /v3/session/{session_id}/update-status/` instead.


        Both body fields are optional and an empty JSON object is accepted.
        `new_status` is stored purely as a label on the entry and accepts any of
        the ten session status values (case-sensitive). `@email` mentions inside
        `comment` are stored as plain text only — they trigger no notifications
        on this endpoint, and the entry's `mentioned_emails` stays empty.


        Note on attribution: entries created here carry no actor information. In
        the feed they show `actor_display: "Unknown"`, `actor_type:
        "CONSOLE_USER"`, and `activity_type: "STATUS_UPDATED"` (storage
        defaults), and the `201` body's `user` field is `"Unknown"`.
      operationId: create_session_review
      parameters:
        - name: Content-Type
          in: header
          required: false
          description: Set to `application/json` when sending a JSON body.
          schema:
            type: string
            example: application/json
        - name: session_id
          in: path
          required: true
          description: >-
            UUID of the User Verification (KYC) session. Must belong to the same
            application as the API key — cross-application UUIDs, deleted
            sessions, and Business Verification (KYB) session IDs all return
            `404`.
          schema:
            type: string
            format: uuid
            example: 8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d
      requestBody:
        required: false
        content:
          application/json:
            schema:
              type: object
              properties:
                new_status:
                  type: string
                  nullable: true
                  enum:
                    - Not Started
                    - In Progress
                    - Approved
                    - Declined
                    - In Review
                    - Expired
                    - Abandoned
                    - Kyc Expired
                    - Resubmitted
                    - Awaiting User
                  description: >-
                    Optional status label to record on the entry. Accepts any
                    session status value, case-sensitive (`"approved"` is
                    rejected with `400`). Recording a status here does **not**
                    change the session's actual status — use the update-status
                    endpoint for that.
                  example: In Review
                comment:
                  type: string
                  nullable: true
                  description: >-
                    Optional free-text note (no enforced length limit). Stored
                    verbatim; `@email` mentions are not processed on this
                    endpoint.
                  example: Flagging for manual document re-check.
            examples:
              NoteWithStatusLabel:
                summary: Note carrying a status label (session status unchanged)
                value:
                  new_status: In Review
                  comment: Flagging for manual document re-check.
              CommentOnly:
                summary: Plain note without a status label
                value:
                  comment: Customer emailed an updated utility bill; awaiting upload.
      responses:
        '201':
          description: >-
            Entry recorded on the session's audit trail. The session itself is
            unchanged — its `status` keeps its previous value and no webhook is
            sent. The new entry also appears at the top of `GET …/reviews/`
            (with `actor_display: "Unknown"`).
          content:
            application/json:
              schema:
                type: object
                properties:
                  user:
                    type: string
                    description: >-
                      Display name of the entry's author. Always `"Unknown"` for
                      entries created through this endpoint, because it records
                      no actor attribution.
                    example: Unknown
                  new_status:
                    type: string
                    nullable: true
                    enum:
                      - Not Started
                      - In Progress
                      - Approved
                      - Declined
                      - In Review
                      - Expired
                      - Abandoned
                      - Kyc Expired
                      - Resubmitted
                      - Awaiting User
                    description: >-
                      The status label recorded on the entry, or `null` if none
                      was sent. The session's actual status is not affected.
                    example: In Review
                  comment:
                    type: string
                    nullable: true
                    description: >-
                      The note recorded on the entry, or `null` if none was
                      sent.
                    example: Flagging for manual document re-check.
                  created_at:
                    type: string
                    format: date-time
                    description: When the entry was written, ISO 8601 UTC.
                    example: '2026-06-12T00:18:44.466537Z'
              examples:
                NoteWithStatusLabel:
                  summary: Note with a status label
                  value:
                    user: Unknown
                    new_status: In Review
                    comment: Flagging for manual document re-check.
                    created_at: '2026-06-12T00:18:44.466537Z'
                CommentOnly:
                  summary: Plain note
                  value:
                    user: Unknown
                    new_status: null
                    comment: Note without status.
                    created_at: '2026-06-12T00:18:44.504157Z'
        '400':
          description: >-
            Validation error, returned as a per-field map of messages. The usual
            cause is a `new_status` value that is not one of the ten allowed
            statuses — the check is case-sensitive.
          content:
            application/json:
              schema:
                type: object
                properties:
                  new_status:
                    type: array
                    items:
                      type: string
                    example:
                      - '"approved" is not a valid choice.'
              examples:
                InvalidStatusChoice:
                  summary: Lowercase / unknown status value
                  value:
                    new_status:
                      - '"approved" is not a valid choice.'
        '403':
          description: >-
            Missing, malformed, expired, or revoked `x-api-key`. Authentication
            is checked before the session lookup, so an invalid key returns
            `403` even when the session UUID does not exist.
          content:
            application/json:
              schema:
                type: object
                properties:
                  detail:
                    type: string
                    example: You do not have permission to perform this action.
              examples:
                InvalidKey:
                  summary: Missing or invalid x-api-key header
                  value:
                    detail: You do not have permission to perform this action.
        '404':
          description: >-
            No User Verification (KYC) session with this UUID is visible to your
            application: the UUID does not exist, the session was deleted, or it
            belongs to a different application. Business Verification (KYB)
            session IDs also return `404` here — the reviews feed is only
            addressable for KYC sessions.
          content:
            application/json:
              schema:
                type: object
                properties:
                  detail:
                    type: string
                    example: Session not found.
              examples:
                NotFound:
                  summary: Unknown, deleted, or cross-application session UUID
                  value:
                    detail: Session not found.
        '429':
          description: >-
            Rate limit exceeded. Inspect `Retry-After` and `X-RateLimit-*`
            response headers. See [Rate limiting](/integration/rate-limiting).
      security:
        - ApiKeyAuth: []
      x-codeSamples:
        - lang: curl
          label: curl
          source: >-
            # Records an audit-trail note only -- the session's status does NOT
            change.

            curl -s -X POST
            https://verification.didit.me/v3/sessions/8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d/reviews/
            \
              -H 'x-api-key: YOUR_API_KEY' \
              -H 'Content-Type: application/json' \
              -d '{
                "new_status": "In Review",
                "comment": "Flagging for manual document re-check."
              }'
        - lang: Python
          label: Python (requests)
          source: >-
            import os, requests


            # Records an audit-trail note only -- the session's status does NOT
            change.

            # To approve/decline a session use PATCH
            /v3/session/{session_id}/update-status/.

            session_id = "8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d"

            resp = requests.post(
                f"https://verification.didit.me/v3/sessions/{session_id}/reviews/",
                headers={
                    "x-api-key": os.environ["DIDIT_API_KEY"],
                    "Content-Type": "application/json",
                },
                json={
                    "new_status": "In Review",
                    "comment": "Flagging for manual document re-check.",
                },
                timeout=10,
            )

            resp.raise_for_status()

            print(resp.json())  # {'user': 'Unknown', 'new_status': 'In Review',
            ...}
        - lang: JavaScript
          label: Node.js (fetch)
          source: >-
            // Records an audit-trail note only -- the session's status does NOT
            change.

            // To approve/decline a session use PATCH
            /v3/session/{session_id}/update-status/.

            const sessionId = "8c2f3a14-7e9b-4d23-9e83-3f7d5e8a1c6d";

            const res = await fetch(
              `https://verification.didit.me/v3/sessions/${sessionId}/reviews/`,
              {
                method: "POST",
                headers: {
                  'x-api-key': 'YOUR_API_KEY',
                  "Content-Type": "application/json",
                },
                body: JSON.stringify({
                  new_status: "In Review",
                  comment: "Flagging for manual document re-check.",
                }),
              },
            );

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

            console.log(await res.json()); // { user: 'Unknown', new_status: 'In
            Review', ... }
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key

````