Files
apocapoc-api/docs/swagger.json
T
david 63dd650755 feat: allow editing frequency and target_value on habits
- Add frequency to UpdateHabitRequest (was immutable, now editable)
- Validate frequency + specific_days/dates coherence on update
- Keep type and is_negative immutable (they change entry semantics)
- Remove completion_rate references from swagger and README
2026-03-08 00:02:37 +01:00

2037 lines
69 KiB
JSON

{
"swagger": "2.0",
"info": {
"description": "Self-hosted habit tracking service",
"title": "Apocapoc API",
"termsOfService": "http://swagger.io/terms/",
"contact": {
"name": "API Support",
"url": "https://github.com/davidfolch/apocapoc-api",
"email": "contact@apocapoc.app"
},
"license": {
"name": "MIT",
"url": "https://opensource.org/licenses/MIT"
}
},
"basePath": "/api/v1",
"paths": {
"/auth/forgot-password": {
"post": {
"description": "Request a password reset email with a reset token",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"auth"
],
"summary": "Request password reset",
"parameters": [
{
"description": "User email",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/http.ForgotPasswordRequest"
}
}
],
"responses": {
"200": {
"description": "Reset email sent successfully",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"400": {
"description": "Invalid email",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"403": {
"description": "Email not verified",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"404": {
"description": "User not found",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"500": {
"description": "Internal server error",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
}
}
}
},
"/auth/login": {
"post": {
"description": "Authenticate user with email and password. Returns both access token and refresh token. The access token is used for API requests, the refresh token is used to obtain new access tokens when they expire.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"auth"
],
"summary": "Login user",
"parameters": [
{
"description": "Login credentials",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/http.LoginRequest"
}
}
],
"responses": {
"200": {
"description": "Returns access token, refresh token, and user ID",
"schema": {
"$ref": "#/definitions/http.AuthResponse"
}
},
"400": {
"description": "Invalid request body",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"401": {
"description": "Invalid email or password",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"403": {
"description": "Email not verified",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"500": {
"description": "Internal server error",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
}
}
}
},
"/auth/logout": {
"post": {
"description": "Revoke the refresh token to invalidate the user session. After logout, the refresh token cannot be used to obtain new access tokens. The user will need to login again. Always call this endpoint before clearing tokens from client storage to ensure proper session termination.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"auth"
],
"summary": "Logout user",
"parameters": [
{
"description": "Refresh token to revoke",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/http.LogoutRequest"
}
}
],
"responses": {
"200": {
"description": "Successfully logged out - the refresh token is now invalid",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"400": {
"description": "Invalid request body or missing refresh token",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"404": {
"description": "Refresh token not found (already revoked or never existed)",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"500": {
"description": "Internal server error",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
}
}
}
},
"/auth/refresh": {
"post": {
"description": "Exchange a valid refresh token for a new access token and refresh token pair. IMPORTANT: The old refresh token is automatically revoked and you receive a NEW refresh token - always update both tokens in storage. Use this endpoint when the access token expires to maintain the user session without requiring re-login.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"auth"
],
"summary": "Refresh access token",
"parameters": [
{
"description": "Current refresh token",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/http.RefreshRequest"
}
}
],
"responses": {
"200": {
"description": "Returns NEW access token and NEW refresh token - the old refresh token is now invalid",
"schema": {
"$ref": "#/definitions/http.AuthResponse"
}
},
"400": {
"description": "Invalid request body",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"401": {
"description": "Invalid or expired refresh token",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"500": {
"description": "Internal server error",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
}
}
}
},
"/auth/register": {
"post": {
"description": "Create a new user account. If email verification is enabled, you will receive a verification email. Otherwise, you can login immediately.",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"auth"
],
"summary": "Register a new user",
"parameters": [
{
"description": "Registration data (password requires: min 8 chars, uppercase, lowercase, digit, special char)",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/http.RegisterRequest"
}
}
],
"responses": {
"201": {
"description": "Returns user ID and message about next steps",
"schema": {
"$ref": "#/definitions/http.RegisterResponse"
}
},
"400": {
"description": "Invalid input: email format or password requirements",
"schema": {
"$ref": "#/definitions/http.ValidationErrorResponse"
}
},
"403": {
"description": "Registration is closed",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"409": {
"description": "Email already registered",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"500": {
"description": "Internal server error",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
}
}
}
},
"/auth/resend-verification": {
"post": {
"description": "Resend the email verification link to the user's email address",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"auth"
],
"summary": "Resend verification email",
"parameters": [
{
"description": "User email",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/http.ResendVerificationRequest"
}
}
],
"responses": {
"200": {
"description": "Verification email sent",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"400": {
"description": "Invalid email",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"404": {
"description": "User not found",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"409": {
"description": "Email already verified",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"500": {
"description": "Internal server error",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
}
}
}
},
"/auth/reset-password": {
"post": {
"description": "Reset user password using the reset token from email",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"auth"
],
"summary": "Reset password",
"parameters": [
{
"description": "Reset token and new password",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/http.ResetPasswordRequest"
}
}
],
"responses": {
"200": {
"description": "Password reset successfully",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"400": {
"description": "Invalid token or password requirements not met",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"404": {
"description": "User not found",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"500": {
"description": "Internal server error",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
}
}
}
},
"/auth/verify-email": {
"post": {
"description": "Verify user email address using the token sent via email",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"auth"
],
"summary": "Verify email address",
"parameters": [
{
"description": "Verification token",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/http.VerifyEmailRequest"
}
}
],
"responses": {
"200": {
"description": "Email verified successfully",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"400": {
"description": "Invalid or expired token",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"409": {
"description": "Email already verified",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"500": {
"description": "Internal server error",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
}
}
}
},
"/export": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"description": "Export all user habits and entries in JSON format with gzip compression. Limited to 1 export per hour.",
"produces": [
"application/json"
],
"tags": [
"export"
],
"summary": "Export user data",
"responses": {
"200": {
"description": "Compressed JSON export",
"schema": {
"$ref": "#/definitions/queries.ExportUserDataResult"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"429": {
"description": "Rate limit exceeded",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"500": {
"description": "Internal server error",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
}
}
}
},
"/habits": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"description": "Get all active habits for the authenticated user with optional pagination and filters",
"produces": [
"application/json"
],
"tags": [
"habits"
],
"summary": "Get all user habits",
"parameters": [
{
"type": "integer",
"description": "Page number (default: 1)",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "Page size (default: 50, max: 100)",
"name": "page_size",
"in": "query"
},
{
"type": "string",
"description": "Filter by type (BOOLEAN, COUNTER, VALUE)",
"name": "type",
"in": "query"
},
{
"type": "string",
"description": "Filter by frequency (DAILY, WEEKLY, MONTHLY)",
"name": "frequency",
"in": "query"
},
{
"type": "boolean",
"description": "Include archived habits (default: false)",
"name": "archived",
"in": "query"
},
{
"type": "string",
"description": "Search by name or description",
"name": "search",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/http.GetUserHabitsResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
}
}
},
"post": {
"security": [
{
"BearerAuth": []
}
],
"description": "Create a new habit for the authenticated user",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"habits"
],
"summary": "Create a new habit",
"parameters": [
{
"description": "Habit data",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/http.CreateHabitRequest"
}
}
],
"responses": {
"201": {
"description": "Created",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
}
}
}
},
"/habits/today": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"description": "Get all habits scheduled for today for the authenticated user. Includes the entry for today if it exists. Requires timezone as query parameter (e.g., ?timezone=America/New_York).",
"produces": [
"application/json"
],
"tags": [
"habits"
],
"summary": "Get today's habits",
"parameters": [
{
"type": "string",
"description": "IANA timezone (e.g., 'America/New_York', 'Europe/Madrid', 'UTC')",
"name": "timezone",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/http.TodaysHabitResponse"
}
}
},
"400": {
"description": "Invalid or missing timezone",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
}
}
}
},
"/habits/{id}": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"description": "Get a specific habit by ID",
"produces": [
"application/json"
],
"tags": [
"habits"
],
"summary": "Get habit by ID",
"parameters": [
{
"type": "string",
"description": "Habit ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/http.UserHabitResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
}
}
},
"put": {
"security": [
{
"BearerAuth": []
}
],
"description": "Update an existing habit",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"habits"
],
"summary": "Update habit",
"parameters": [
{
"type": "string",
"description": "Habit ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Update data",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/http.UpdateHabitRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
}
}
},
"delete": {
"security": [
{
"BearerAuth": []
}
],
"description": "Archive (soft delete) a habit",
"produces": [
"application/json"
],
"tags": [
"habits"
],
"summary": "Archive habit",
"parameters": [
{
"type": "string",
"description": "Habit ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
}
}
}
},
"/habits/{id}/entries": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"description": "Get entries (completion history) for a habit with optional date filtering and pagination",
"produces": [
"application/json"
],
"tags": [
"habits"
],
"summary": "Get habit entries",
"parameters": [
{
"type": "string",
"description": "Habit ID",
"name": "id",
"in": "path",
"required": true
},
{
"type": "string",
"description": "Start date (YYYY-MM-DD)",
"name": "from",
"in": "query"
},
{
"type": "string",
"description": "End date (YYYY-MM-DD)",
"name": "to",
"in": "query"
},
{
"type": "integer",
"description": "Page number",
"name": "page",
"in": "query"
},
{
"type": "integer",
"description": "Page size (max 100)",
"name": "limit",
"in": "query"
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/http.HabitEntriesResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
}
}
}
},
"/habits/{id}/entries/{date}": {
"delete": {
"security": [
{
"BearerAuth": []
}
],
"description": "Delete a habit entry (unmark completion)",
"produces": [
"application/json"
],
"tags": [
"habits"
],
"summary": "Unmark habit",
"parameters": [
{
"type": "string",
"description": "Habit ID",
"name": "id",
"in": "path",
"required": true
},
{
"type": "string",
"description": "Date (YYYY-MM-DD)",
"name": "date",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
}
}
}
},
"/habits/{id}/mark": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"description": "Mark a habit as completed for a specific date",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"habits"
],
"summary": "Mark habit as complete",
"parameters": [
{
"type": "string",
"description": "Habit ID",
"name": "id",
"in": "path",
"required": true
},
{
"description": "Mark data",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/http.MarkHabitRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"409": {
"description": "Conflict",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
}
}
}
},
"/health": {
"get": {
"description": "Get API health status including database connectivity and uptime",
"produces": [
"application/json"
],
"tags": [
"system"
],
"summary": "Health check",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/http.HealthResponse"
}
},
"503": {
"description": "Service Unavailable",
"schema": {
"$ref": "#/definitions/http.HealthResponse"
}
}
}
}
},
"/stats/habits/{id}": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"description": "Get statistics for a specific habit including streaks and completions",
"produces": [
"application/json"
],
"tags": [
"stats"
],
"summary": "Get habit statistics",
"parameters": [
{
"type": "string",
"description": "Habit ID",
"name": "id",
"in": "path",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/queries.HabitStatsDTO"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"403": {
"description": "Forbidden",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"404": {
"description": "Not Found",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
}
}
}
},
"/sync/batch": {
"post": {
"security": [
{
"BearerAuth": []
}
],
"description": "Apply a batch of changes from the client for offline sync (Last-Write-Wins)",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"sync"
],
"summary": "Apply sync batch",
"parameters": [
{
"description": "Sync batch data",
"name": "request",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/http.SyncBatchRequest"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
}
}
}
},
"/sync/changes": {
"get": {
"security": [
{
"BearerAuth": []
}
],
"description": "Get all changes (habits and entries) since a given timestamp for offline sync",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"sync"
],
"summary": "Get sync changes",
"parameters": [
{
"type": "string",
"description": "ISO 8601 timestamp (e.g., 2025-01-01T00:00:00Z)",
"name": "since",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/http.SyncChangesResponse"
}
},
"400": {
"description": "Bad Request",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"401": {
"description": "Unauthorized",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
}
}
}
},
"/users/me": {
"delete": {
"security": [
{
"BearerAuth": []
}
],
"description": "Permanently delete the authenticated user's account and all associated data (habits, entries, tokens). This action cannot be undone.",
"produces": [
"application/json"
],
"tags": [
"users"
],
"summary": "Delete user account",
"responses": {
"200": {
"description": "Account deleted successfully",
"schema": {
"type": "object",
"additionalProperties": {
"type": "string"
}
}
},
"401": {
"description": "Unauthorized - invalid or missing token",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"404": {
"description": "User not found",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
},
"500": {
"description": "Internal server error",
"schema": {
"$ref": "#/definitions/http.ErrorResponse"
}
}
}
}
}
},
"definitions": {
"http.AuthResponse": {
"type": "object",
"properties": {
"refresh_token": {
"type": "string"
},
"token": {
"type": "string"
},
"user_id": {
"type": "string"
}
}
},
"http.CreateHabitRequest": {
"type": "object",
"properties": {
"carry_over": {
"type": "boolean"
},
"description": {
"type": "string"
},
"frequency": {
"$ref": "#/definitions/value_objects.Frequency"
},
"is_negative": {
"type": "boolean"
},
"name": {
"type": "string"
},
"specific_dates": {
"type": "array",
"items": {
"type": "integer"
}
},
"specific_days": {
"type": "array",
"items": {
"type": "integer"
}
},
"target_value": {
"type": "number"
},
"type": {
"$ref": "#/definitions/value_objects.HabitType"
}
}
},
"http.EntryChangesDTO": {
"type": "object",
"properties": {
"created": {
"type": "array",
"items": {
"$ref": "#/definitions/http.SyncHabitEntryDTO"
}
},
"deleted": {
"type": "array",
"items": {
"type": "string"
}
},
"updated": {
"type": "array",
"items": {
"$ref": "#/definitions/http.SyncHabitEntryDTO"
}
}
}
},
"http.ErrorResponse": {
"type": "object",
"properties": {
"error": {
"type": "string"
}
}
},
"http.ForgotPasswordRequest": {
"type": "object",
"properties": {
"email": {
"type": "string"
}
}
},
"http.GetUserHabitsResponse": {
"type": "object",
"properties": {
"data": {
"type": "array",
"items": {
"$ref": "#/definitions/http.UserHabitResponse"
}
},
"pagination": {
"$ref": "#/definitions/pagination.Response"
}
}
},
"http.HabitChangesDTO": {
"type": "object",
"properties": {
"created": {
"type": "array",
"items": {
"$ref": "#/definitions/http.SyncHabitDTO"
}
},
"deleted": {
"type": "array",
"items": {
"type": "string"
}
},
"updated": {
"type": "array",
"items": {
"$ref": "#/definitions/http.SyncHabitDTO"
}
}
}
},
"http.HabitEntriesResponse": {
"type": "object",
"properties": {
"entries": {
"type": "array",
"items": {
"$ref": "#/definitions/http.HabitEntryResponse"
}
},
"limit": {
"type": "integer"
},
"page": {
"type": "integer"
},
"total": {
"type": "integer"
}
}
},
"http.HabitEntryResponse": {
"type": "object",
"properties": {
"completed_at": {
"type": "string"
},
"habit_id": {
"type": "string"
},
"id": {
"type": "string"
},
"scheduled_date": {
"type": "string"
},
"value": {
"type": "number"
}
}
},
"http.HealthResponse": {
"type": "object",
"properties": {
"database": {
"type": "string"
},
"smtp": {
"type": "string"
},
"status": {
"type": "string"
},
"uptime": {
"type": "string"
}
}
},
"http.LoginRequest": {
"type": "object",
"properties": {
"email": {
"type": "string"
},
"password": {
"type": "string"
}
}
},
"http.LogoutRequest": {
"type": "object",
"properties": {
"refresh_token": {
"type": "string"
}
}
},
"http.MarkHabitRequest": {
"type": "object",
"properties": {
"scheduled_date": {
"type": "string"
},
"value": {
"type": "number"
}
}
},
"http.RefreshRequest": {
"type": "object",
"properties": {
"refresh_token": {
"type": "string"
}
}
},
"http.RegisterRequest": {
"type": "object",
"properties": {
"email": {
"type": "string"
},
"password": {
"type": "string"
}
}
},
"http.RegisterResponse": {
"type": "object",
"properties": {
"message": {
"type": "string"
},
"user_id": {
"type": "string"
}
}
},
"http.ResendVerificationRequest": {
"type": "object",
"properties": {
"email": {
"type": "string"
}
}
},
"http.ResetPasswordRequest": {
"type": "object",
"properties": {
"new_password": {
"type": "string"
},
"token": {
"type": "string"
}
}
},
"http.SyncBatchRequest": {
"type": "object",
"properties": {
"entries": {
"$ref": "#/definitions/http.EntryChangesDTO"
},
"habits": {
"$ref": "#/definitions/http.HabitChangesDTO"
}
}
},
"http.SyncChangesResponse": {
"type": "object",
"properties": {
"entries": {
"$ref": "#/definitions/http.EntryChangesDTO"
},
"habits": {
"$ref": "#/definitions/http.HabitChangesDTO"
}
}
},
"http.SyncHabitDTO": {
"type": "object",
"properties": {
"archived_at": {
"type": "string"
},
"carry_over": {
"type": "boolean"
},
"created_at": {
"type": "string"
},
"description": {
"type": "string"
},
"frequency": {
"$ref": "#/definitions/value_objects.Frequency"
},
"id": {
"type": "string"
},
"is_negative": {
"type": "boolean"
},
"name": {
"type": "string"
},
"specific_dates": {
"type": "array",
"items": {
"type": "integer"
}
},
"specific_days": {
"type": "array",
"items": {
"type": "integer"
}
},
"target_value": {
"type": "number"
},
"type": {
"$ref": "#/definitions/value_objects.HabitType"
},
"updated_at": {
"type": "string"
},
"user_id": {
"type": "string"
}
}
},
"http.SyncHabitEntryDTO": {
"type": "object",
"properties": {
"completed_at": {
"type": "string"
},
"habit_id": {
"type": "string"
},
"id": {
"type": "string"
},
"scheduled_date": {
"type": "string"
},
"updated_at": {
"type": "string"
},
"value": {
"type": "number"
}
}
},
"http.TodaysHabitEntryResponse": {
"type": "object",
"properties": {
"completed_at": {
"type": "string"
},
"id": {
"type": "string"
},
"value": {
"type": "number"
}
}
},
"http.TodaysHabitResponse": {
"type": "object",
"properties": {
"entry": {
"$ref": "#/definitions/http.TodaysHabitEntryResponse"
},
"id": {
"type": "string"
},
"is_carried_over": {
"type": "boolean"
},
"is_negative": {
"type": "boolean"
},
"name": {
"type": "string"
},
"scheduled_date": {
"type": "string"
},
"target_value": {
"type": "number"
},
"type": {
"$ref": "#/definitions/value_objects.HabitType"
}
}
},
"http.UpdateHabitRequest": {
"type": "object",
"properties": {
"carry_over": {
"type": "boolean"
},
"description": {
"type": "string"
},
"frequency": {
"$ref": "#/definitions/value_objects.Frequency"
},
"is_negative": {
"type": "boolean"
},
"name": {
"type": "string"
},
"specific_dates": {
"type": "array",
"items": {
"type": "integer"
}
},
"specific_days": {
"type": "array",
"items": {
"type": "integer"
}
},
"target_value": {
"type": "number"
}
}
},
"http.UserHabitResponse": {
"type": "object",
"properties": {
"carry_over": {
"type": "boolean"
},
"frequency": {
"$ref": "#/definitions/value_objects.Frequency"
},
"id": {
"type": "string"
},
"is_negative": {
"type": "boolean"
},
"name": {
"type": "string"
},
"specific_days": {
"type": "array",
"items": {
"type": "integer"
}
},
"target_value": {
"type": "number"
},
"type": {
"$ref": "#/definitions/value_objects.HabitType"
}
}
},
"http.ValidationErrorResponse": {
"type": "object",
"properties": {
"error": {
"type": "string"
},
"field": {
"type": "string"
}
}
},
"http.VerifyEmailRequest": {
"type": "object",
"properties": {
"token": {
"type": "string"
}
}
},
"pagination.Response": {
"type": "object",
"properties": {
"page": {
"type": "integer"
},
"page_size": {
"type": "integer"
},
"total_items": {
"type": "integer"
},
"total_pages": {
"type": "integer"
}
}
},
"queries.ExportEntryDTO": {
"type": "object",
"properties": {
"completed_at": {
"type": "string"
},
"habit_id": {
"type": "string"
},
"id": {
"type": "string"
},
"scheduled_date": {
"type": "string"
},
"value": {
"type": "number"
}
}
},
"queries.ExportHabitDTO": {
"type": "object",
"properties": {
"archived_at": {
"type": "string"
},
"carry_over": {
"type": "boolean"
},
"created_at": {
"type": "string"
},
"description": {
"type": "string"
},
"frequency": {
"$ref": "#/definitions/value_objects.Frequency"
},
"id": {
"type": "string"
},
"is_negative": {
"type": "boolean"
},
"name": {
"type": "string"
},
"specific_dates": {
"type": "array",
"items": {
"type": "integer"
}
},
"specific_days": {
"type": "array",
"items": {
"type": "integer"
}
},
"target_value": {
"type": "number"
},
"type": {
"$ref": "#/definitions/value_objects.HabitType"
}
}
},
"queries.ExportUserDataResult": {
"type": "object",
"properties": {
"entries": {
"type": "array",
"items": {
"$ref": "#/definitions/queries.ExportEntryDTO"
}
},
"exported_at": {
"type": "string"
},
"habits": {
"type": "array",
"items": {
"$ref": "#/definitions/queries.ExportHabitDTO"
}
}
}
},
"queries.HabitStatsDTO": {
"type": "object",
"properties": {
"completions_this_month": {
"type": "integer"
},
"completions_this_week": {
"type": "integer"
},
"current_streak": {
"type": "integer"
},
"habit_id": {
"type": "string"
},
"habit_name": {
"type": "string"
},
"longest_streak": {
"type": "integer"
},
"total_completions": {
"type": "integer"
}
}
},
"value_objects.Frequency": {
"type": "string",
"enum": [
"DAILY",
"WEEKLY",
"MONTHLY"
],
"x-enum-varnames": [
"FrequencyDaily",
"FrequencyWeekly",
"FrequencyMonthly"
]
},
"value_objects.HabitType": {
"type": "string",
"enum": [
"BOOLEAN",
"COUNTER",
"VALUE"
],
"x-enum-varnames": [
"HabitTypeBoolean",
"HabitTypeCounter",
"HabitTypeValue"
]
}
},
"securityDefinitions": {
"BearerAuth": {
"description": "Type \"Bearer\" followed by a space and JWT token.",
"type": "apiKey",
"name": "Authorization",
"in": "header"
}
}
}