2026-10-23 12:24:54 +00:00
openapi : 3.0 .3
info :
title : Kererū Forms API
version : 1.0 .0
description : |
API for **Kererū forms**, a simple survey/form capture system.
2026-10-23 23:57:57 +00:00
Forms are versioned and can be inactivated.
A form contains an ordered set of
2026-10-23 12:24:54 +00:00
questions whose answers are one of : true /false, numeric, a drop-down selection,
2026-10-23 23:57:57 +00:00
date or free-form text.
Responses are identified by a user identifier captured at the
2026-10-23 12:24:54 +00:00
start of the form. A form may optionally require a single-use access code.
contact :
name : Kererū Forms
email : support@kereru.forms
license :
name : MIT
servers :
- url : http://localhost:8080
description : Local development
- url : http://localhost:8080/api/v1
description : Local development (v1 base)
tags :
- name : system
description : Health and meta endpoints
- name : forms
description : Form definition and management
- name : submissions
description : Form submissions and management of captured data
- name : access-codes
description : Single-use access codes
2026-10-23 19:36:02 +00:00
- name : auth
description : Password fallback and OIDC-backed bearer authentication
2026-10-23 12:24:54 +00:00
paths :
/health :
get :
tags : [ system]
operationId : getHealth
summary : Service health
responses :
'200' :
description : Service is healthy
content :
application/json :
schema :
$ref : '#/components/schemas/Health'
/forms :
get :
tags : [ forms]
operationId : listForms
summary : List forms
description : Returns form summaries. Use `includeInactive` to include inactivated forms (management view).
parameters :
- name : includeInactive
in : query
required : false
schema :
type : boolean
default : false
- name : activeOnly
in : query
required : false
deprecated : true
schema :
type : boolean
responses :
'200' :
description : A list of forms
content :
application/json :
schema :
type : array
items :
$ref : '#/components/schemas/FormSummary'
'500' :
$ref : '#/components/responses/Error'
post :
tags : [ forms]
operationId : createForm
summary : Create a form
2026-10-23 19:36:02 +00:00
security :
- bearerAuth : [ ]
2026-10-23 12:24:54 +00:00
requestBody :
required : true
content :
application/json :
schema :
$ref : '#/components/schemas/CreateFormRequest'
responses :
'201' :
description : Form created
content :
application/json :
schema :
$ref : '#/components/schemas/Form'
'400' :
$ref : '#/components/responses/Error'
'500' :
$ref : '#/components/responses/Error'
/forms/{formId}:
parameters :
- $ref : '#/components/parameters/FormId'
get :
tags : [ forms]
operationId : getForm
summary : Get a form by id
responses :
'200' :
description : The form
content :
application/json :
schema :
$ref : '#/components/schemas/Form'
'404' :
$ref : '#/components/responses/Error'
'500' :
$ref : '#/components/responses/Error'
put :
tags : [ forms]
operationId : updateForm
summary : Update a form definition
2026-10-23 19:36:02 +00:00
security :
- bearerAuth : [ ]
2026-10-23 12:24:54 +00:00
requestBody :
required : true
content :
application/json :
schema :
$ref : '#/components/schemas/UpdateFormRequest'
responses :
'200' :
description : Updated form
content :
application/json :
schema :
$ref : '#/components/schemas/Form'
'400' :
$ref : '#/components/responses/Error'
'404' :
$ref : '#/components/responses/Error'
'500' :
$ref : '#/components/responses/Error'
delete :
tags : [ forms]
operationId : deleteForm
summary : Delete a form definition
2026-10-23 19:36:02 +00:00
security :
- bearerAuth : [ ]
2026-10-23 12:24:54 +00:00
responses :
'204' :
description : Deleted
'404' :
$ref : '#/components/responses/Error'
'500' :
$ref : '#/components/responses/Error'
/forms/{formId}/inactivate:
parameters :
- $ref : '#/components/parameters/FormId'
post :
tags : [ forms]
operationId : inactivateForm
summary : Inactivate a form
2026-10-23 19:36:02 +00:00
security :
- bearerAuth : [ ]
2026-10-23 12:24:54 +00:00
description : Inactivated forms remain visible in management but cannot accept new submissions.
responses :
'200' :
description : Inactivated form
content :
application/json :
schema :
$ref : '#/components/schemas/Form'
'404' :
$ref : '#/components/responses/Error'
'500' :
$ref : '#/components/responses/Error'
/forms/{formId}/activate:
parameters :
- $ref : '#/components/parameters/FormId'
post :
tags : [ forms]
operationId : activateForm
summary : Activate a form
2026-10-23 19:36:02 +00:00
security :
- bearerAuth : [ ]
2026-10-23 12:24:54 +00:00
responses :
'200' :
description : Activated form
content :
application/json :
schema :
$ref : '#/components/schemas/Form'
'404' :
$ref : '#/components/responses/Error'
'500' :
$ref : '#/components/responses/Error'
/forms/{formId}/versions:
parameters :
- $ref : '#/components/parameters/FormId'
post :
tags : [ forms]
operationId : createFormVersion
summary : Create a new version of a form
2026-10-23 19:36:02 +00:00
security :
- bearerAuth : [ ]
2026-10-23 12:24:54 +00:00
description : Clones the form and its questions into a new version with the version number incremented. The new version is active; the previous version is inactivated.
responses :
'201' :
description : New form version
content :
application/json :
schema :
$ref : '#/components/schemas/Form'
'404' :
$ref : '#/components/responses/Error'
'500' :
$ref : '#/components/responses/Error'
/forms/{formId}/pdf:
parameters :
- $ref : '#/components/parameters/FormId'
get :
tags : [ forms]
operationId : getFormPdf
summary : Download a PDF rendering of the form definition
responses :
'200' :
description : PDF document generated from the form definition
content :
application/pdf :
schema :
type : string
format : binary
'404' :
$ref : '#/components/responses/Error'
'500' :
$ref : '#/components/responses/Error'
/forms/{formId}/submissions:
parameters :
- $ref : '#/components/parameters/FormId'
get :
tags : [ submissions]
operationId : listFormSubmissions
summary : List submissions for a form (management)
2026-10-23 19:36:02 +00:00
security :
- bearerAuth : [ ]
2026-10-23 12:24:54 +00:00
responses :
'200' :
description : Submissions
content :
application/json :
schema :
type : array
items :
$ref : '#/components/schemas/SubmissionSummary'
'404' :
$ref : '#/components/responses/Error'
'500' :
$ref : '#/components/responses/Error'
post :
tags : [ submissions]
operationId : submitForm
summary : Submit an answer set for a form
description : |
Captures a completed form. The `userId` (captured at the start of the form)
identifies the respondent. If the form requires an access code, a valid,
unused `accessCode` must be supplied and is consumed on success.
requestBody :
required : true
content :
application/json :
schema :
$ref : '#/components/schemas/SubmitFormRequest'
responses :
'201' :
description : Submission accepted
content :
application/json :
schema :
$ref : '#/components/schemas/Submission'
'400' :
$ref : '#/components/responses/Error'
'404' :
$ref : '#/components/responses/Error'
'409' :
$ref : '#/components/responses/Error'
'410' :
$ref : '#/components/responses/Error'
'500' :
$ref : '#/components/responses/Error'
/submissions/{submissionId}:
parameters :
- name : submissionId
in : path
required : true
schema :
type : string
format : uuid
get :
tags : [ submissions]
operationId : getSubmission
summary : Get a submission by id (management)
2026-10-23 19:36:02 +00:00
security :
- bearerAuth : [ ]
2026-10-23 12:24:54 +00:00
responses :
'200' :
description : Submission
content :
application/json :
schema :
$ref : '#/components/schemas/Submission'
'404' :
$ref : '#/components/responses/Error'
'500' :
$ref : '#/components/responses/Error'
/submissions/{submissionId}/offline-pdf:
parameters :
- name : submissionId
in : path
required : true
schema :
type : string
format : uuid
get :
tags : [ submissions]
operationId : getSubmissionOfflinePdf
summary : Download the offline PDF uploaded with a submission
2026-10-23 19:36:02 +00:00
security :
- bearerAuth : [ ]
2026-10-23 12:24:54 +00:00
responses :
'200' :
description : The uploaded PDF
content :
application/pdf :
schema :
type : string
format : binary
'404' :
$ref : '#/components/responses/Error'
'500' :
$ref : '#/components/responses/Error'
/forms/{formId}/access-codes:
parameters :
- $ref : '#/components/parameters/FormId'
get :
tags : [ access-codes]
operationId : listAccessCodes
summary : List access codes for a form
2026-10-23 19:36:02 +00:00
security :
- bearerAuth : [ ]
2026-10-23 12:24:54 +00:00
responses :
'200' :
description : Access codes
content :
application/json :
schema :
type : array
items :
$ref : '#/components/schemas/AccessCode'
'404' :
$ref : '#/components/responses/Error'
'500' :
$ref : '#/components/responses/Error'
post :
tags : [ access-codes]
operationId : createAccessCodes
summary : Create one or more access codes for a form
2026-10-23 19:36:02 +00:00
security :
- bearerAuth : [ ]
2026-10-23 12:24:54 +00:00
requestBody :
required : true
content :
application/json :
schema :
$ref : '#/components/schemas/CreateAccessCodesRequest'
responses :
'201' :
description : Created access codes
content :
application/json :
schema :
type : array
items :
$ref : '#/components/schemas/AccessCode'
'400' :
$ref : '#/components/responses/Error'
'404' :
$ref : '#/components/responses/Error'
'500' :
$ref : '#/components/responses/Error'
2026-10-23 19:36:02 +00:00
/auth/config :
get :
tags : [ auth]
operationId : getAuthConfig
summary : Get browser authentication configuration
description : |
When `mode` is `oidc`, the SPA performs an authorization-code flow with
PKCE against the configured provider and then presents the returned ID
token as a bearer token to this API. When `mode` is `password`, the SPA
uses `/auth/login`.
responses :
'200' :
description : Authentication configuration
content :
application/json :
schema :
$ref : '#/components/schemas/AuthConfig'
/auth/login :
post :
tags : [ auth]
operationId : login
summary : Log in with the local password fallback
description : Only available when no OIDC issuer is configured.
requestBody :
required : true
content :
application/json :
schema :
$ref : '#/components/schemas/LoginRequest'
responses :
'200' :
description : Signed local JWT
content :
application/json :
schema :
$ref : '#/components/schemas/LoginResponse'
'400' :
$ref : '#/components/responses/Error'
'401' :
$ref : '#/components/responses/Error'
/auth/me :
get :
tags : [ auth]
operationId : getCurrentUser
summary : Get the current authenticated user
security :
- bearerAuth : [ ]
responses :
'200' :
description : Current authentication state
content :
application/json :
schema :
$ref : '#/components/schemas/AuthMeResponse'
'401' :
$ref : '#/components/responses/Error'
/auth/logout :
post :
tags : [ auth]
operationId : logout
summary : Log out
description : Stateless tokens are discarded by the client; this endpoint exists for symmetry.
security :
- bearerAuth : [ ]
responses :
'204' :
description : Logged out
2026-10-23 12:24:54 +00:00
components :
2026-10-23 19:36:02 +00:00
securitySchemes :
bearerAuth :
type : http
scheme : bearer
bearerFormat : JWT
description : |
Local password-mode JWT, or an OIDC ID token obtained by the SPA using
the authorization-code flow with PKCE.
2026-10-23 12:24:54 +00:00
parameters :
FormId :
name : formId
in : path
required : true
schema :
type : string
format : uuid
responses :
Error :
description : Error response
content :
application/json :
schema :
$ref : '#/components/schemas/Error'
schemas :
2026-10-23 19:36:02 +00:00
AuthConfig :
type : object
required : [ mode, requireSubmissionAuth]
properties :
mode :
type : string
enum : [ password, oidc]
requireSubmissionAuth :
type : boolean
oidc :
$ref : '#/components/schemas/OIDCClientConfig'
OIDCClientConfig :
type : object
required : [ issuerUrl, authorizationEndpoint, tokenEndpoint, clientId, scopes]
properties :
issuerUrl :
type : string
format : uri
authorizationEndpoint :
type : string
format : uri
tokenEndpoint :
type : string
format : uri
clientId :
type : string
scopes :
type : array
items :
type : string
LoginRequest :
type : object
required : [ password]
properties :
password :
type : string
format : password
LoginResponse :
type : object
required : [ token, user]
properties :
token :
type : string
user :
$ref : '#/components/schemas/AuthUser'
AuthUser :
type : object
required : [ id, roles, canAdmin, canView]
properties :
id :
type : string
name :
type : string
email :
type : string
format : email
roles :
type : array
items :
type : string
canAdmin :
type : boolean
canView :
type : boolean
AuthMeResponse :
type : object
required : [ authenticated]
properties :
authenticated :
type : boolean
user :
$ref : '#/components/schemas/AuthUser'
2026-10-23 12:24:54 +00:00
Health :
type : object
required : [ status, version]
properties :
status :
type : string
example : ok
version :
type : string
example : 1.0 .0
uptimeSeconds :
type : integer
format : int64
QuestionType :
type : string
description : |
* `boolean` – true/false
* `number` – numeric answer
* `select` – one option from a configured list
* `text` – free-form text (single or multiline)
* `date` - calendar date in `YYYY-MM-DD` form
enum : [ boolean, number, select, text, date]
QuestionOption :
type : object
required : [ value, label]
properties :
value :
type : string
label :
type : string
2026-10-25 10:53:17 +00:00
Section :
type : object
required : [ id, identifier]
properties :
id :
type : string
format : uuid
identifier :
type : string
description : Section identifier such as letter or number used to prefix question numbers
title :
type : string
description :
type : string
SectionInput :
type : object
required : [ identifier]
properties :
identifier :
type : string
title :
type : string
description :
type : string
2026-10-23 12:24:54 +00:00
Question :
type : object
required : [ id, position, key, label, type, required]
properties :
id :
type : string
format : uuid
position :
type : integer
key :
type : string
description : Stable machine key for this question within the form
label :
type : string
description : The question text (may be multiline, no formatting directives)
description :
type : string
type :
$ref : '#/components/schemas/QuestionType'
required :
type : boolean
options :
type : array
description : Present for `select` questions
items :
$ref : '#/components/schemas/QuestionOption'
minValue :
type : number
description : Minimum allowed value for `number` questions
maxValue :
type : number
description : Maximum allowed value for `number` questions
placeholder :
type : string
multiline :
type : boolean
description : Whether a `text` answer may span multiple lines
2026-10-25 10:53:17 +00:00
sectionId :
type : string
format : uuid
description : Identifier of the section this question belongs to
2026-10-23 12:24:54 +00:00
FormSummary :
type : object
required : [ id, formGroupId, version, title, isActive, questionCount, createdAt, updatedAt]
properties :
id :
type : string
format : uuid
formGroupId :
type : string
format : uuid
description : Groups all versions of the same logical form
version :
type : integer
title :
type : string
description :
type : string
logoUrl :
type : string
pdfUrl :
type : string
description : Optional absolute URL of a prebuilt PDF form linked to this definition
externalReferenceRequired :
type : boolean
description : Whether a reference identifier is required before the form can be submitted
externalReferenceLabel :
type : string
description : Label shown for the external reference, for example Invoice number
isActive :
type : boolean
questionCount :
type : integer
createdAt :
type : string
format : date-time
updatedAt :
type : string
format : date-time
Form :
type : object
required : [ id, formGroupId, version, title, isActive, requiresAccessCode, questions, createdAt, updatedAt]
properties :
id :
type : string
format : uuid
formGroupId :
type : string
format : uuid
version :
type : integer
title :
type : string
description :
type : string
introduction :
type : string
description : Introduction section explaining purpose, background and support details
isActive :
type : boolean
requiresAccessCode :
type : boolean
signatureRequired :
type : boolean
description : Whether a digital signature is captured at the end of the form
logoUrl :
type : string
pdfUrl :
type : string
description : Optional absolute URL of a prebuilt PDF form linked to this definition
externalReferenceRequired :
type : boolean
description : Whether a reference identifier is required before the form can be submitted
externalReferenceLabel :
type : string
description : Label shown for the external reference, for example Invoice number
companyName :
type : string
contactEmail :
type : string
contactPhone :
type : string
contactUrl :
type : string
questions :
type : array
items :
$ref : '#/components/schemas/Question'
2026-10-25 10:53:17 +00:00
sections :
type : array
items :
$ref : '#/components/schemas/Section'
2026-10-23 12:24:54 +00:00
createdAt :
type : string
format : date-time
updatedAt :
type : string
format : date-time
QuestionInput :
type : object
required : [ key, label, type, required]
properties :
key :
type : string
label :
type : string
description :
type : string
type :
$ref : '#/components/schemas/QuestionType'
required :
type : boolean
options :
type : array
items :
$ref : '#/components/schemas/QuestionOption'
minValue :
type : number
maxValue :
type : number
placeholder :
type : string
multiline :
type : boolean
2026-10-25 10:53:17 +00:00
sectionId :
type : string
format : uuid
description : Identifier of the section this question belongs to
2026-10-23 12:24:54 +00:00
CreateFormRequest :
type : object
required : [ title, questions]
properties :
title :
type : string
description :
type : string
introduction :
type : string
requiresAccessCode :
type : boolean
signatureRequired :
type : boolean
logoUrl :
type : string
pdfUrl :
type : string
externalReferenceRequired :
type : boolean
externalReferenceLabel :
type : string
companyName :
type : string
contactEmail :
type : string
contactPhone :
type : string
contactUrl :
type : string
questions :
type : array
items :
$ref : '#/components/schemas/QuestionInput'
2026-10-25 10:53:17 +00:00
sections :
type : array
items :
$ref : '#/components/schemas/SectionInput'
2026-10-23 12:24:54 +00:00
UpdateFormRequest :
type : object
required : [ title, questions]
properties :
title :
type : string
description :
type : string
introduction :
type : string
requiresAccessCode :
type : boolean
signatureRequired :
type : boolean
logoUrl :
type : string
pdfUrl :
type : string
externalReferenceRequired :
type : boolean
externalReferenceLabel :
type : string
companyName :
type : string
contactEmail :
type : string
contactPhone :
type : string
contactUrl :
type : string
questions :
type : array
items :
$ref : '#/components/schemas/QuestionInput'
2026-10-25 10:53:17 +00:00
sections :
type : array
items :
$ref : '#/components/schemas/SectionInput'
2026-10-23 12:24:54 +00:00
UserIdType :
type : string
2026-10-23 23:57:57 +00:00
enum : [ email, username, phone, foreignkey guid]
2026-10-23 12:24:54 +00:00
Answer :
type : object
required : [ questionId, value]
properties :
questionId :
type : string
format : uuid
value :
description : Answer value. String, number, boolean or list of strings depending on the question type.
SubmitFormRequest :
type : object
required : [ userId, userIdType, answers]
properties :
userId :
type : string
2026-10-23 23:57:57 +00:00
description : Respondent identifier captured at the start (email, username, phone, foreignkey or GUID)
2026-10-23 12:24:54 +00:00
userIdType :
$ref : '#/components/schemas/UserIdType'
accessCode :
type : string
description : Required when the form has `requiresAccessCode = true`
externalReference :
type : string
description : |-
External reference identifier used to link this response to a record in
another system, for example an invoice, account or policy number.
answers :
type : array
items :
$ref : '#/components/schemas/Answer'
signature :
$ref : '#/components/schemas/SignatureInput'
offlinePdf :
type : string
description : |
Base64 data URL of a PDF completed offline (for example
`data:application/pdf;base64,...`). Maximum decoded size is 10 MB.
offlinePdfName :
type : string
description : Original filename of the uploaded offline PDF
tracking :
$ref : '#/components/schemas/ClientTracking'
ClientTracking :
type : object
description : Client supplied tracking details. IP and user agent are captured server-side.
properties :
locale :
type : string
timezone :
type : string
place :
type : string
referer :
type : string
SignatureType :
type : string
description : |
* `drawn` - signature drawn in the browser
* `image` - uploaded image containing a signature
enum : [ drawn, image]
SignatureInput :
type : object
required : [ type, dataUrl, name]
properties :
type :
$ref : '#/components/schemas/SignatureType'
dataUrl :
type : string
description : PNG or JPEG image data URL (maximum decoded size 2 MB)
name :
type : string
description : Name of the person signing
title :
type : string
description : Optional title of the person signing
Signature :
type : object
required : [ type, dataUrl, name, signedAt]
properties :
type :
$ref : '#/components/schemas/SignatureType'
dataUrl :
type : string
name :
type : string
title :
type : string
signedAt :
type : string
format : date-time
AnswerRecord :
type : object
required : [ questionId, questionKey, value]
properties :
questionId :
type : string
format : uuid
questionKey :
type : string
value :
description : The captured value
Tracking :
type : object
properties :
ipAddress :
type : string
userAgent :
type : string
locale :
type : string
timezone :
type : string
place :
type : string
referer :
type : string
Submission :
type : object
required : [ id, formId, formVersion, userId, userIdType, answers, submittedAt]
properties :
id :
type : string
format : uuid
formId :
type : string
format : uuid
formVersion :
type : integer
userId :
type : string
userIdType :
$ref : '#/components/schemas/UserIdType'
externalReference :
type : string
answers :
type : array
items :
$ref : '#/components/schemas/AnswerRecord'
signature :
$ref : '#/components/schemas/Signature'
offlinePdfName :
type : string
description : Present when the respondent uploaded a completed offline PDF
submittedAt :
type : string
format : date-time
tracking :
$ref : '#/components/schemas/Tracking'
SubmissionSummary :
type : object
required : [ id, formId, formVersion, userId, userIdType, submittedAt]
properties :
id :
type : string
format : uuid
formId :
type : string
format : uuid
formVersion :
type : integer
userId :
type : string
userIdType :
$ref : '#/components/schemas/UserIdType'
externalReference :
type : string
submittedAt :
type : string
format : date-time
AccessCode :
type : object
required : [ id, formId, code, used, createdAt]
properties :
id :
type : string
format : uuid
formId :
type : string
format : uuid
code :
type : string
used :
type : boolean
usedAt :
type : string
format : date-time
createdAt :
type : string
format : date-time
CreateAccessCodesRequest :
type : object
required : [ count]
properties :
count :
type : integer
minimum : 1
maximum : 100
prefix :
type : string
Error :
type : object
required : [ error]
properties :
error :
type : string
code :
type : string