Skip to content

API Reference

FormVox provides a REST API for programmatic access to forms and responses.

Authentication

Internal API

For authenticated Nextcloud users: - Uses Nextcloud session cookies - Or basic authentication with app password

# Using app password
curl -u "username:app-password" \
  https://your-nextcloud.com/apps/formvox/api/forms

Public API

For public form submissions: - No authentication required - Uses form's public hash

curl -X POST \
  https://your-nextcloud.com/apps/formvox/api/public/HASH/submit \
  -d '{"answers": {...}}'

Endpoints

Forms

List Forms

GET /apps/formvox/api/forms

Response:

{
  "forms": [
    {
      "id": "file-id",
      "title": "Customer Survey",
      "created": "2024-01-15T10:00:00Z",
      "responseCount": 42
    }
  ]
}

Get Form

GET /apps/formvox/api/forms/{fileId}

Parameters: | Name | Type | Description | |------|------|-------------| | fileId | integer | Nextcloud file ID |

Response:

{
  "form": {
    "id": "uuid",
    "title": "Customer Survey",
    "description": "...",
    "questions": [ ... ],
    "settings": { ... }
  }
}

Create Form

POST /apps/formvox/api/forms

Request Body:

{
  "title": "New Form",
  "path": "/Forms",
  "template": "blank"
}

Response:

{
  "fileId": 12345,
  "path": "/Forms/New Form.fvform"
}

Update Form

PUT /apps/formvox/api/forms/{fileId}

Request Body:

{
  "form": {
    "title": "Updated Title",
    "settings": { ... }
  },
  "questions": [ ... ]
}

Delete Form

DELETE /apps/formvox/api/forms/{fileId}

Questions

Add Question

POST /apps/formvox/api/forms/{fileId}/questions

Request Body:

{
  "type": "text",
  "title": "What is your name?",
  "required": true
}

Update Question

PUT /apps/formvox/api/forms/{fileId}/questions/{questionId}

Delete Question

DELETE /apps/formvox/api/forms/{fileId}/questions/{questionId}

Reorder Questions

PUT /apps/formvox/api/forms/{fileId}/questions/order

Request Body:

{
  "order": ["q3", "q1", "q2"]
}

Responses

Get Responses

GET /apps/formvox/api/forms/{fileId}/responses

Query Parameters: | Name | Type | Default | Description | |------|------|---------|-------------| | page | integer | 1 | Page number | | limit | integer | 50 | Results per page | | from | string | - | Start date (ISO 8601) | | to | string | - | End date (ISO 8601) |

Response:

{
  "responses": [ ... ],
  "total": 142,
  "page": 1,
  "pages": 3
}

Get Single Response

GET /apps/formvox/api/forms/{fileId}/responses/{responseId}

Delete Response

DELETE /apps/formvox/api/forms/{fileId}/responses/{responseId}

Delete All Responses

DELETE /apps/formvox/api/forms/{fileId}/responses

Public Submission

Get Public Form

GET /apps/formvox/api/public/{hash}

Returns form structure without responses.

Response:

{
  "form": {
    "title": "...",
    "description": "...",
    "questions": [ ... ],
    "branding": { ... }
  }
}

Submit Response

POST /apps/formvox/api/public/{hash}/submit

Request Body:

{
  "answers": {
    "q1": "John Doe",
    "q2": "c1",
    "q3": ["c1", "c2"]
  }
}

Response:

{
  "success": true,
  "message": "Response submitted successfully"
}

With Password:

POST /apps/formvox/api/public/{hash}/submit
Authorization: Bearer {password}

Export

Export Responses

GET /apps/formvox/api/forms/{fileId}/export

Query Parameters: | Name | Type | Options | Description | |------|------|---------|-------------| | format | string | csv, json, xlsx | Export format | | from | string | - | Start date | | to | string | - | End date |

Response: Returns file download with appropriate content type.

Sharing

Get Share Settings

GET /apps/formvox/api/forms/{fileId}/share

Update Share Settings

PUT /apps/formvox/api/forms/{fileId}/share

Request Body:

{
  "isPublic": true,
  "password": "optional-password",
  "expiresAt": "2024-12-31T23:59:59Z"
}

Statistics

Get Form Statistics

GET /apps/formvox/api/forms/{fileId}/stats

Response:

{
  "totalResponses": 142,
  "responsesPerDay": [ ... ],
  "averageCompletionTime": 180,
  "questionStats": { ... }
}

Error Responses

All errors return JSON with consistent format:

{
  "error": true,
  "message": "Form not found",
  "code": "FORM_NOT_FOUND"
}

Error Codes

Code HTTP Status Description
UNAUTHORIZED 401 Authentication required
FORBIDDEN 403 No permission
FORM_NOT_FOUND 404 Form doesn't exist
INVALID_REQUEST 400 Bad request data
VALIDATION_ERROR 422 Form validation failed
RATE_LIMITED 429 Too many requests
FORM_EXPIRED 410 Public link expired
PASSWORD_REQUIRED 401 Form requires password

Rate Limiting

Public endpoints are rate limited:

Endpoint Limit
Public form view 60/minute
Public submission 10/minute

Authenticated endpoints: | Endpoint | Limit | |----------|-------| | All endpoints | 120/minute |

Rate limit headers:

X-RateLimit-Limit: 60
X-RateLimit-Remaining: 45
X-RateLimit-Reset: 1705329600

Presence (Collaborative Editing)

Endpoints for real-time collaborative editing presence detection.

Send Presence Heartbeat

POST /apps/formvox/api/form/{fileId}/presence

Registers the current user as actively editing the form. Should be called every 30 seconds as a heartbeat.

Response:

{
  "status": "ok"
}

Get Active Editors

GET /apps/formvox/api/form/{fileId}/presence

Returns a list of users currently editing the form (active within the last 60 seconds).

Response:

{
  "editors": [
    {
      "userId": "jane",
      "displayName": "Jane Doe"
    }
  ],
  "myFirstSeen": 1709740800
}

Notes: - The current user is excluded from the editors list - Users who haven't sent a heartbeat in 60 seconds are considered inactive - myFirstSeen indicates when the current user first started editing (used for edit priority/locking)

External API & Webhooks

FormVox now supports an External API with API key authentication and webhooks for real-time notifications.

See the dedicated documentation: External API & Webhooks

Features: - API key authentication (bcrypt-hashed storage) - Configurable permissions per key - CRUD operations on responses - Webhook events: response.created, response.updated, response.deleted - HMAC-SHA256 signed webhook payloads

Code Examples

Python

import requests

# Authentication
session = requests.Session()
session.auth = ('username', 'app-password')

# List forms
response = session.get('https://nc.example.com/apps/formvox/api/forms')
forms = response.json()['forms']

# Get responses
response = session.get(f'https://nc.example.com/apps/formvox/api/forms/{form_id}/responses')
responses = response.json()['responses']

# Export to CSV
response = session.get(
    f'https://nc.example.com/apps/formvox/api/forms/{form_id}/export',
    params={'format': 'csv'}
)
with open('responses.csv', 'wb') as f:
    f.write(response.content)

JavaScript

// Using fetch
const BASE_URL = 'https://nc.example.com/apps/formvox/api';

async function getForms() {
  const response = await fetch(`${BASE_URL}/forms`, {
    credentials: 'include'
  });
  return response.json();
}

async function submitPublicForm(hash, answers) {
  const response = await fetch(`${BASE_URL}/public/${hash}/submit`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ answers })
  });
  return response.json();
}

cURL

# List forms
curl -u "user:app-password" \
  https://nc.example.com/apps/formvox/api/forms

# Create form
curl -u "user:app-password" \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{"title": "New Survey", "path": "/"}' \
  https://nc.example.com/apps/formvox/api/forms

# Submit public form
curl -X POST \
  -H "Content-Type: application/json" \
  -d '{"answers": {"q1": "Test"}}' \
  https://nc.example.com/apps/formvox/api/public/abc123/submit

Next Steps