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

# Questionnaires API: Upload, Status, Results

> Submit security questionnaires and RFPs via the Wolfia API, track autofill status, and retrieve results programmatically, ideal for JIRA/ServiceNow workflows.

## Upload a file

**URL:** `POST https://api.wolfia.com/v1/questionnaire/upload/file`

**Authentication:** API key required (see [API overview](/how-to/api-overview) for setup)

This endpoint accepts multipart form data.

```bash theme={null}
curl -X POST https://api.wolfia.com/v1/questionnaire/upload/file \
  -H "X-API-Key: wolfia-api-YOUR_KEY_HERE" \
  -F "file=@questionnaire.xlsx;type=application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" \
  -F "company_name=Acme Corp" \
  -F "deal_value=50000" \
  -F "deadline=2025-03-15" \
  -F "instructions=Focus on SOC 2 and ISO 27001 compliance"
```

<Warning>
  You **must** set the correct MIME type for the file using the `;type=` syntax in curl (or the equivalent `Content-Type` in your HTTP client). Uploads with an incorrect MIME type will fail to process.
</Warning>

### Request parameters

| Parameter        | Type              | Required | Description                                                                    |
| ---------------- | ----------------- | -------- | ------------------------------------------------------------------------------ |
| `file`           | file              | Yes      | The questionnaire file                                                         |
| `company_name`   | string            | Yes      | Company that sent the questionnaire                                            |
| `deal_value`     | decimal           | No       | Associated deal value                                                          |
| `deadline`       | date (YYYY-MM-DD) | No       | Completion deadline                                                            |
| `instructions`   | string            | No       | Instructions to guide AI processing                                            |
| `tag_ids`        | array of strings  | No       | Tag IDs to categorize the questionnaire (see [Content tags](/how-to/api-tags)) |
| `assignee_ids`   | array of strings  | No       | User IDs to assign to                                                          |
| `assignee_types` | array of strings  | No       | Type per assignee (`USER` or `GROUP`)                                          |

### Supported file types

| Extension | MIME type                                                                 |
| --------- | ------------------------------------------------------------------------- |
| `.xlsx`   | `application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`       |
| `.xlsm`   | `application/vnd.ms-excel.sheet.macroenabled.12`                          |
| `.xls`    | `application/vnd.ms-excel`                                                |
| `.csv`    | `text/csv`                                                                |
| `.docx`   | `application/vnd.openxmlformats-officedocument.wordprocessingml.document` |
| `.pdf`    | `application/pdf`                                                         |

### Response (200 OK)

```json theme={null}
{
  "questionnaire_id": "da891171-9293-4218-a74a-c53c55673beb",
  "message": "File 'questionnaire.xlsx' uploaded successfully and queued for processing"
}
```

## Upload text content

**URL:** `POST https://api.wolfia.com/v1/questionnaire/upload/text`

**Authentication:** API key required (see [API overview](/how-to/api-overview) for setup)

Use this when you have questions as text rather than a file, for example, pasted from an email or extracted from a web form.

```bash theme={null}
curl -X POST https://api.wolfia.com/v1/questionnaire/upload/text \
  -H "X-API-Key: wolfia-api-YOUR_KEY_HERE" \
  -H "Content-Type: application/json" \
  -d '{
    "text_content": "1. Describe your data encryption standards.\n2. How do you handle incident response?\n3. What certifications do you hold?",
    "company_name": "Acme Corp"
  }'
```

### Request parameters

| Parameter      | Type              | Required | Description                                                                    |
| -------------- | ----------------- | -------- | ------------------------------------------------------------------------------ |
| `text_content` | string            | Yes      | Questions as text (50 to 200,000 characters)                                   |
| `company_name` | string            | No       | Company that sent the questionnaire                                            |
| `source_type`  | string            | No       | Defaults to `TEXT`                                                             |
| `deal_value`   | decimal           | No       | Associated deal value                                                          |
| `deadline`     | date (YYYY-MM-DD) | No       | Completion deadline                                                            |
| `instructions` | string            | No       | Instructions to guide AI processing                                            |
| `tag_ids`      | array of strings  | No       | Tag IDs to categorize the questionnaire (see [Content tags](/how-to/api-tags)) |
| `assignees`    | array of objects  | No       | Each with `id` (string) and `type` (`USER` or `GROUP`)                         |

### Response (200 OK)

```json theme={null}
{
  "questionnaire_id": "da891171-9293-4218-a74a-c53c55673beb",
  "message": "Text content uploaded successfully and queued for processing"
}
```

## List questionnaires

**URL:** `GET https://api.wolfia.com/v1/questionnaire/list`

**Authentication:** API key required

Returns a paginated list of all questionnaires in your organization, ordered by creation date (newest first).

### Request

```bash theme={null}
curl -X GET 'https://api.wolfia.com/v1/questionnaire/list?limit=20&offset=0' \
  -H "X-API-Key: wolfia-api-YOUR_KEY_HERE"
```

### Query parameters

| Parameter | Type             | Required | Default | Description                                       |
| --------- | ---------------- | -------- | ------- | ------------------------------------------------- |
| `limit`   | integer (1-1000) | No       | 10      | Number of questionnaires to return                |
| `offset`  | integer          | No       | 0       | Number of questionnaires to skip (for pagination) |

### Response (200 OK)

```json theme={null}
{
  "questionnaires": [
    {
      "id": "da891171-9293-4218-a74a-c53c55673beb",
      "title": "SOC 2 Security Questionnaire",
      "company_name": "Acme Corp",
      "status": "ANSWERING_COMPLETED",
      "status_display_name": "Ready for review",
      "total_questions": 150,
      "answered_questions": 148,
      "created_at": 1735689600000000,
      "deadline": "2025-03-15",
      "deal_value": 50000,
      "tags": [
        {
          "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
          "name": "SOC 2",
          "description": "SOC 2 compliance questionnaires",
          "is_ai_generated": false,
          "status": "ACTIVE"
        }
      ],
      "should_poll": false
    }
  ],
  "pagination": {
    "total": 42,
    "limit": 20,
    "offset": 0
  },
  "should_poll": false
}
```

<Note>
  The `should_poll` field (at both the list level and individual questionnaire level) indicates whether any questionnaires are still being processed. If `true`, poll the endpoint periodically to get updated statuses.
</Note>

### Processing status values

After uploading, a questionnaire moves through these statuses:

| Status                            | Display name       | Meaning                                         |
| --------------------------------- | ------------------ | ----------------------------------------------- |
| `UPLOADED`                        | Uploaded           | File received, processing not started           |
| `QUEUED_PENDING_REVIEW`           | Pre-validation     | Queued for initial validation before processing |
| `QUEUED_FOR_EXTRACTION`           | Queued             | Validated and queued for question extraction    |
| `ANALYSIS_IN_PROGRESS`            | Analyzing          | AI is analyzing the document structure          |
| `QUESTION_EXTRACTION_IN_PROGRESS` | Extracting         | AI is extracting individual questions           |
| `ANSWERING_IN_PROGRESS`           | Answering          | AI is generating answers                        |
| `ANSWERING_COMPLETED`             | Ready for review   | All answers generated, ready for human review   |
| `READY_FOR_APPROVAL`              | Ready for approval | Reviewed and pending final approval             |
| `COMPLETED`                       | Completed          | Fully completed                                 |

Failed statuses (`ANALYSIS_FAILED`, `QUESTION_EXTRACTION_FAILED`, `ANSWERING_FAILED`) indicate a processing error. Contact [support@wolfia.com](mailto:support@wolfia.com) if failures persist.

## Search questionnaires

**URL:** `GET https://api.wolfia.com/v1/questionnaire/search`

**Authentication:** API key required

Search questionnaires by text or filter by specific fields using the `@field:value` syntax.

### Request

```bash theme={null}
# Text search
curl -X GET 'https://api.wolfia.com/v1/questionnaire/search?query=security+review' \
  -H "X-API-Key: wolfia-api-YOUR_KEY_HERE"

# Filter by status
curl -X GET 'https://api.wolfia.com/v1/questionnaire/search?query=@status:ANSWERING_COMPLETED' \
  -H "X-API-Key: wolfia-api-YOUR_KEY_HERE"

# Filter by account name
curl -X GET 'https://api.wolfia.com/v1/questionnaire/search?query=@account:Acme' \
  -H "X-API-Key: wolfia-api-YOUR_KEY_HERE"

# Combine filters
curl -X GET 'https://api.wolfia.com/v1/questionnaire/search?query=@status:ANSWERING_IN_PROGRESS+@account:Acme' \
  -H "X-API-Key: wolfia-api-YOUR_KEY_HERE"
```

### Query parameters

| Parameter | Type             | Required | Default | Description                            |
| --------- | ---------------- | -------- | ------- | -------------------------------------- |
| `query`   | string           | No       | `""`    | Search query (see filter syntax below) |
| `limit`   | integer (1-1000) | No       | 10      | Number of results to return            |
| `offset`  | integer          | No       | 0       | Pagination offset                      |

### Filter syntax

Use `@field:value` to filter by specific fields. Multiple filters can be combined in a single query.

| Filter      | Example                       | Description                                                              |
| ----------- | ----------------------------- | ------------------------------------------------------------------------ |
| `@title:`   | `@title:security review`      | Match questionnaire title                                                |
| `@status:`  | `@status:ANSWERING_COMPLETED` | Match processing status (see [status values](#processing-status-values)) |
| `@account:` | `@account:Acme`               | Match company name                                                       |

Plain text without the `@` prefix searches across all fields.

### Response (200 OK)

The response format matches [List questionnaires](#list-questionnaires), with an additional `matched_questions` array on each item showing which questions matched the search query.

## Get questionnaire details

**URL:** `GET https://api.wolfia.com/v1/questionnaire/{questionnaire_id}/overview`

**Authentication:** API key required

Returns detailed information about a specific questionnaire, including per-sheet statistics, processing logs, and completion metrics.

### Request

```bash theme={null}
curl -X GET 'https://api.wolfia.com/v1/questionnaire/da891171-9293-4218-a74a-c53c55673beb/overview' \
  -H "X-API-Key: wolfia-api-YOUR_KEY_HERE"
```

### Response (200 OK)

```json theme={null}
{
  "id": "da891171-9293-4218-a74a-c53c55673beb",
  "title": "SOC 2 Security Questionnaire",
  "company_name": "Acme Corp",
  "status": "ANSWERING_COMPLETED",
  "status_display_name": "Ready for review",
  "total_questions": 150,
  "answered_questions": 148,
  "statistics": {
    "total_questions": 150,
    "answered_questions": 148,
    "approved_questions": 0,
    "ai_answered_questions": 145,
    "manual_answered_questions": 3,
    "not_answered_questions": 2,
    "completion_percentage": 98.67,
    "approval_percentage": 0.0
  },
  "created_at": 1735689600000000,
  "deadline": "2025-03-15",
  "deal_value": 50000,
  "tags": [
    {
      "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "name": "SOC 2",
      "description": "SOC 2 compliance questionnaires",
      "is_ai_generated": false,
      "status": "ACTIVE"
    }
  ],
  "sheets": [
    {
      "id": "sheet-uuid",
      "name": "Security Controls",
      "status": "ANSWERING_COMPLETED",
      "should_poll": false
    }
  ],
  "sheet_stats": [
    {
      "id": "sheet-uuid",
      "name": "Security Controls",
      "status": "ANSWERING_COMPLETED",
      "total_questions": 150,
      "answered_questions": 148,
      "completion_percentage": 98.67
    }
  ],
  "should_poll": false
}
```

<Note>
  Use `should_poll` to determine if the questionnaire is still being processed. If `true`, poll this endpoint at reasonable intervals (e.g., every 10-30 seconds) until processing completes.
</Note>

### Key response fields

| Field                 | Type          | Description                                                                |
| --------------------- | ------------- | -------------------------------------------------------------------------- |
| `id`                  | string (UUID) | Questionnaire identifier                                                   |
| `status`              | string        | Current processing status (see [status values](#processing-status-values)) |
| `status_display_name` | string        | Human-readable status                                                      |
| `total_questions`     | integer       | Total extracted questions                                                  |
| `answered_questions`  | integer       | Questions with AI or manual answers                                        |
| `statistics`          | object        | Detailed completion and quality metrics                                    |
| `sheets`              | array         | Individual sheets/tabs with their processing status                        |
| `sheet_stats`         | array         | Per-sheet question and completion statistics                               |
| `tags`                | array         | Content tags applied to this questionnaire                                 |
| `should_poll`         | boolean       | `true` if processing is still in progress                                  |
| `created_at`          | integer       | Creation timestamp (microseconds)                                          |

## Integration example

### Python: Upload and poll until complete

```python theme={null}
import os
import time

import requests

API_KEY = os.environ['WOLFIA_API_KEY']
BASE_URL = 'https://api.wolfia.com/v1'


def upload_and_wait(file_path, company_name, timeout_seconds=600):
    """Upload a questionnaire and poll until processing completes."""
    headers = {'X-API-Key': API_KEY}

    # Upload the file
    with open(file_path, 'rb') as f:
        resp = requests.post(
            f'{BASE_URL}/questionnaire/upload/file',
            headers=headers,
            files={'file': (os.path.basename(file_path), f, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')},
            data={'company_name': company_name},
            timeout=60,
        )
    resp.raise_for_status()
    qid = resp.json()['questionnaire_id']
    print(f'Uploaded: {qid}')

    # Poll for completion
    start = time.time()
    while time.time() - start < timeout_seconds:
        overview = requests.get(
            f'{BASE_URL}/questionnaire/{qid}/overview',
            headers=headers,
            timeout=30,
        )
        overview.raise_for_status()
        data = overview.json()

        status = data['status']
        display = data['status_display_name']
        print(f'  Status: {display} ({status})')

        if not data['should_poll']:
            return data

        time.sleep(15)

    raise TimeoutError(f'Questionnaire {qid} did not complete within {timeout_seconds}s')


if __name__ == '__main__':
    result = upload_and_wait('questionnaire.xlsx', 'Acme Corp')
    stats = result['statistics']
    print(f"Done: {stats['answered_questions']}/{stats['total_questions']} answered ({stats['completion_percentage']:.1f}%)")
```

### Python: Ingest questionnaires from JIRA

```python theme={null}
import mimetypes
import os

import requests
from jira import JIRA

WOLFIA_API_KEY = os.environ["WOLFIA_API_KEY"]
BASE_URL = "https://api.wolfia.com/v1"

MIME_TYPES = {
    ".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
    ".xlsm": "application/vnd.ms-excel.sheet.macroenabled.12",
    ".xls": "application/vnd.ms-excel",
    ".csv": "text/csv",
    ".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
    ".pdf": "application/pdf",
}


def get_mime_type(filename):
    ext = os.path.splitext(filename)[1].lower()
    return MIME_TYPES.get(ext, mimetypes.guess_type(filename)[0])


def process_questionnaire_tickets():
    jira = JIRA(server=os.environ["JIRA_SERVER"], token_auth=os.environ["JIRA_TOKEN"])
    issues = jira.search_issues('project = SEC AND type = "Security Questionnaire" AND status = "To Do"', maxResults=50)

    for issue in issues:
        company = issue.fields.summary.split(" - ")[0] if " - " in issue.fields.summary else issue.fields.summary

        if issue.fields.attachment:
            att = issue.fields.attachment[0]
            mime_type = get_mime_type(att.filename)
            resp = requests.post(
                f"{BASE_URL}/questionnaire/upload/file",
                headers={"X-API-Key": WOLFIA_API_KEY},
                files={"file": (att.filename, att.get(), mime_type)},
                data={"company_name": company, "instructions": issue.fields.description or ""},
                timeout=60,
            )
        elif issue.fields.description and len(issue.fields.description) >= 50:
            resp = requests.post(
                f"{BASE_URL}/questionnaire/upload/text",
                headers={"X-API-Key": WOLFIA_API_KEY, "Content-Type": "application/json"},
                json={"text_content": issue.fields.description, "company_name": company},
                timeout=30,
            )
        else:
            print(f"Skipping {issue.key}: no attachment or usable description")
            continue

        if resp.status_code == 200:
            qid = resp.json()["questionnaire_id"]
            jira.add_comment(issue, f"Uploaded to Wolfia (ID: {qid})")
            jira.transition_issue(issue, "In Progress")
            print(f"  {issue.key} -> {qid}")
        else:
            print(f"  {issue.key} failed: {resp.text}")


if __name__ == "__main__":
    process_questionnaire_tickets()
```

## Error responses

| Status Code | What it means                            | How to fix                                                                |
| ----------- | ---------------------------------------- | ------------------------------------------------------------------------- |
| 400         | Unsupported file type                    | Use a supported format: `.xlsx`, `.xlsm`, `.xls`, `.csv`, `.docx`, `.pdf` |
| 401         | Invalid or expired API key               | Verify your API key                                                       |
| 403         | Insufficient permissions                 | Check the API key owner's role                                            |
| 404         | Questionnaire not found                  | Verify the questionnaire ID exists                                        |
| 422         | Missing required fields or invalid input | Check required fields. Text must be 50-200,000 characters.                |
| 500         | Internal error                           | Retry with exponential backoff                                            |

## Best practices

<AccordionGroup>
  <Accordion title="File vs text upload">
    Use **file upload** when you have the original document (XLSX, PDF, DOCX). Wolfia preserves the document structure.

    Use **text upload** for plain text questions from emails, web forms, or chat messages.
  </Accordion>

  <Accordion title="Use instructions to guide AI">
    The `instructions` field shapes how Wolfia fills in answers:

    ```json theme={null}
    {
      "instructions": "This is a SOC 2 questionnaire. Reference our latest audit report."
    }
    ```
  </Accordion>

  <Accordion title="Set metadata for better tracking">
    Include `company_name`, `deal_value`, and `deadline` when available. This metadata appears in the Wolfia dashboard for prioritization.
  </Accordion>

  <Accordion title="Poll efficiently after upload">
    After uploading, use the [get questionnaire details](#get-questionnaire-details) endpoint to track progress. Check the `should_poll` field; when it's `false`, processing is complete. A polling interval of 15-30 seconds is recommended.
  </Accordion>
</AccordionGroup>

## Getting help

* **Check file types:** `.xlsx`, `.xlsm`, `.xls`, `.csv`, `.docx`, `.pdf`
* **Check text length:** 50 to 200,000 characters
* **Contact support:** Email [support@wolfia.com](mailto:support@wolfia.com)

## Next steps

<CardGroup cols={2}>
  <Card title="Content tags" icon="tags" href="/how-to/api-tags">
    List available tags to categorize questionnaires on upload
  </Card>

  <Card title="API overview" icon="code" href="/how-to/api-overview">
    Authentication, rate limits, and error handling
  </Card>

  <Card title="Knowledge base upload" icon="file-arrow-up" href="/how-to/api-knowledge-upload">
    Add documents to your knowledge base
  </Card>

  <Card title="Trust portal analytics" icon="chart-mixed" href="/how-to/api-trust-portal-analytics">
    Track engagement metrics and account activity
  </Card>
</CardGroup>
