# Interview Lifecycle API Reference {#interview-lifecycle-api-reference}


**Version:** 1.0  
**Last Updated:** 2026-07-16

All endpoints are located under `/api/v1/interviews/` and require the `X-Tenant-ID` header for tenant scoping.

---


## Create Interview {#create-interview}



### Purpose {#purpose}


Initialize a new interview session for a survey respondent.


### URL {#url}


`/api/v1/interviews`


### Method {#method}


`POST`


### Authentication {#authentication}


Bearer token authentication required.


### Permissions {#permissions}


`forms:session:create`


### Request Body {#request-body}


```json
{
  "survey_id": "uuid",
  "session_token": "string",
  "respondent_id": "uuid (optional)",
  "total_questions": 50,
  "survey_name": "string (optional)",
  "interview_metadata": {}
}
```

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `survey_id` | UUID | Yes | Survey to conduct |
| `session_token` | string | Yes | Unique session identifier |
| `respondent_id` | UUID | No | Respondent being interviewed |
| `total_questions` | integer | No | Expected question count |
| `survey_name` | string | No | Display name |
| `interview_metadata` | object | No | Additional metadata |


### Response {#response}


```json
{
  "id": "uuid",
  "tenant_id": "uuid",
  "survey_id": "uuid",
  "session_token": "string",
  "status": "created",
  "started_at": null,
  "paused_at": null,
  "completed_at": null,
  "terminated_at": null,
  "current_page": 0,
  "answered_count": 0,
  "created_at": "ISO8601"
}
```


### Error Responses {#error-responses}


| Status | Description |
|--------|-------------|
| 400 | Missing tenant ID or invalid survey |
| 401 | Authentication required |
| 403 | Insufficient permissions |
| 500 | Internal server error |


### Example Request {#example-request}


```bash
curl -X POST "http://form-service:8010/api/v1/interviews" \
  -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..." \
  -H "X-Tenant-ID: 550e8400-e29b-41d4-a716-446655440000" \
  -H "Content-Type: application/json" \
  -d '{
    "survey_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "session_token": "interview_abc123",
    "respondent_id": "r1e2s3p4-o5n6d7e8t9t0"
  }'
```

---


## List Interviews {#list-interviews}



### Purpose {#purpose-1}


Retrieve paginated list of interviews with optional filtering.


### URL {#url-1}


`/api/v1/interviews`


### Method {#method-1}


`GET`


### Authentication {#authentication-1}


Bearer token authentication required.


### Permissions {#permissions-1}


`forms:session:read`


### Request Parameters {#request-parameters}


| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `status` | string | No | Filter by status |
| `survey_id` | UUID | No | Filter by survey |
| `interviewer_id` | UUID | No | Filter by interviewer |
| `respondent_id` | UUID | No | Filter by respondent |
| `search` | string | No | Search by session token |
| `sort` | string | No | Sort field |
| `skip` | integer | No | Default: 0 |
| `limit` | integer | No | Default: 20, max: 100 |


### Response {#response-1}


```json
{
  "items": [...],
  "total": 100,
  "skip": 0,
  "limit": 20,
  "has_next": true,
  "has_previous": false
}
```

---


## Get Interview {#get-interview}



### Purpose {#purpose-2}


Retrieve details of a specific interview.


### URL {#url-2}


`/api/v1/interviews/{interview_id}`


### Method {#method-2}


`GET`


### Authentication {#authentication-2}


Bearer token authentication required.


### Permissions {#permissions-2}


`forms:session:read`


### Response {#response-2}


```json
{
  "id": "uuid",
  "tenant_id": "uuid",
  "survey_id": "uuid",
  "respondent_id": "uuid",
  "interviewer_id": "uuid",
  "session_token": "string",
  "status": "in_progress",
  "started_at": "ISO8601",
  "last_heartbeat_at": "ISO8601",
  "current_page": 15,
  "answered_count": 15,
  "total_questions": 50,
  "created_at": "ISO8601",
  "updated_at": "ISO8601"
}
```


### Error Responses {#error-responses-1}


| Status | Description |
|--------|-------------|
| 400 | Missing tenant ID |
| 401 | Authentication required |
| 403 | Insufficient permissions |
| 404 | Interview not found |

---


## Update Interview {#update-interview}



### Purpose {#purpose-3}


Update interview metadata and progress.


### URL {#url-3}


`/api/v1/interviews/{interview_id}`


### Method {#method-3}


`PATCH`


### Authentication {#authentication-3}


Bearer token authentication required.


### Permissions {#permissions-3}


`forms:session:read`


### Request Body {#request-body-1}


```json
{
  "current_page": 20,
  "answered_count": 20,
  "total_questions": 50,
  "interview_metadata": {}
}
```

| Field | Type | Description |
|-------|------|-------------|
| `current_page` | integer | Current navigation position |
| `answered_count` | integer | Questions answered |
| `total_questions` | integer | Total questions in survey |
| `interview_metadata` | object | Additional metadata |


### Response {#response-3}


Updated InterviewDto.


### Error Responses {#error-responses-2}


| Status | Description |
|--------|-------------|
| 400 | Invalid data or missing tenant ID |
| 409 | Concurrent modification detected |

---


## Transition Interview {#transition-interview}



### Purpose {#purpose-4}


Change interview status through valid lifecycle transitions.


### URL {#url-4}


`/api/v1/interviews/{interview_id}/transition`


### Method {#method-4}


`POST`


### Authentication {#authentication-4}


Bearer token authentication required.


### Permissions {#permissions-4}


`forms:session:read`


### Request Body {#request-body-2}


```json
{
  "to_status": "in_progress",
  "form_data": {},
  "is_complete": false,
  "current_page": 50,
  "version_id": "uuid (optional)",
  "edit_response_id": "uuid (optional)"
}
```


### Valid Transitions {#valid-transitions}


```
created → in_progress
in_progress → paused
in_progress → completed
in_progress → terminated
completed → needs_revision
needs_revision → in_progress
```


### Response {#response-4}


Updated InterviewDto.


### Error Responses {#error-responses-3}


| Status | Description |
|--------|-------------|
| 400 | Invalid status or transition not allowed |
| 404 | Interview not found |

---


## Interview Heartbeat {#interview-heartbeat}



### Purpose {#purpose-5}


Ping to indicate interview is still active.


### URL {#url-5}


`/api/v1/interviews/{interview_id}/heartbeat`


### Method {#method-5}


`POST`


### Authentication {#authentication-5}


Bearer token authentication required.


### Permissions {#permissions-5}


`forms:session:read`


### Response {#response-5}


```json
{
  "status": "in_progress",
  "heartbeat_at": "2026-07-16T10:30:00.000Z"
}
```


### Error Responses {#error-responses-4}


| Status | Description |
|--------|-------------|
| 400 | Interview not heartbeat-eligible |
| 404 | Interview not found |

---


## Get Termination Details {#get-termination-details}



### Purpose {#purpose-6}


Retrieve termination information for a terminated interview.


### URL {#url-6}


`/api/v1/interviews/{interview_id}/termination`


### Method {#method-6}


`GET`


### Authentication {#authentication-6}


Bearer token authentication required.


### Permissions {#permissions-6}


`forms:session:read`


### Response {#response-6}


```json
{
  "id": "uuid",
  "interview_id": "uuid",
  "terminated_by": "uuid",
  "terminated_at": "ISO8601",
  "termination_reason": "refused_to_continue",
  "explanatory_text": "Respondent declined to answer sensitive questions",
  "data_saved": true,
  "retry_submission_available": false
}
```


### Error Responses {#error-responses-5}


| Status | Description |
|--------|-------------|
| 404 | No termination record found |

---


## List Termination Policies {#list-termination-policies}



### Purpose {#purpose-7}


Retrieve available termination reason options and policies.


### URL {#url-7}


`/api/v1/interviews/termination-policies`


### Method {#method-7}


`GET`


### Authentication {#authentication-7}


Bearer token authentication required.


### Permissions {#permissions-7}


`forms:session:read`


### Response {#response-7}


```json
[
  {
    "value": "refused_to_continue",
    "label": "Refused to Continue",
    "description": "Respondent declined to continue the interview"
  }
]
```

---


## Retry Submission {#retry-submission}



### Purpose {#purpose-8}


Retry submission for a terminated interview with partial data.


### URL {#url-8}


`/api/v1/interviews/{interview_id}/retry-submission`


### Method {#method-8}


`POST`


### Authentication {#authentication-8}


Bearer token authentication required.


### Permissions {#permissions-8}


`forms:session:read`


### Response {#response-8}


Updated TerminationDto.

---


## Delete Interview {#delete-interview}



### Purpose {#purpose-9}


Soft-delete an interview (only if status is "created").


### URL {#url-9}


`/api/v1/interviews/{interview_id}`


### Method {#method-9}


`DELETE`


### Authentication {#authentication-9}


Bearer token authentication required.


### Permissions {#permissions-9}


`forms:session:delete`


### Response {#response-9}


No content (204) on success.


### Error Responses {#error-responses-6}


| Status | Description |
|--------|-------------|
| 400 | Interview not in "created" status |
| 403 | Insufficient permissions |

---


## List Interview Events {#list-interview-events}



### Purpose {#purpose-10}


Retrieve audit trail of interview state changes.


### URL {#url-10}


`/api/v1/interviews/{interview_id}/events`


### Method {#method-10}


`GET`


### Authentication {#authentication-10}


Bearer token authentication required.


### Permissions {#permissions-10}


`forms:session:read`


### Response {#response-10}


```json
[
  {
    "id": "uuid",
    "interview_id": "uuid",
    "event_type": "interview.started",
    "to_status": "in_progress",
    "actor_id": "uuid",
    "created_at": "ISO8601"
  }
]
```

---


## Resolve Interview by Session Token {#resolve-interview-by-session-token}



### Purpose {#purpose-11}


Look up an interview using its session token.


### URL {#url-11}


`/api/v1/interviews/resolve/by-session-token/{session_token}`


### Method {#method-11}


`GET`


### Authentication {#authentication-11}


Bearer token authentication required.


### Permissions {#permissions-11}


`forms:session:read`


### Response {#response-11}


InterviewDto for the matching interview.


### Error Responses {#error-responses-7}


| Status | Description |
|--------|-------------|
| 404 | Interview not found |
