{"openapi":"3.1.0","info":{"title":"GRTforms API","version":"1.0.0","description":"\nThe GRTforms API provides programmatic access to your forms and submissions.\n\n## Authentication\n\nAll API requests require authentication using an API key. Include your API key in the `Authorization` header:\n\n```\nAuthorization: Bearer grt_live_<prefix>_<secret>\n```\n\n## Rate Limits\n\nAPI requests are rate limited to 100 requests per minute per API key. Rate limit headers are included in all responses:\n\n- `X-RateLimit-Remaining`: Requests remaining in the current window\n- `X-RateLimit-Reset`: Unix timestamp when the limit resets\n- `Retry-After`: Seconds to wait before retrying (only on 429 responses)\n\n## Scopes\n\nAPI keys can have the following scopes:\n\n| Scope | Description |\n|-------|-------------|\n| `forms:read` | Read form metadata |\n| `forms:write` | Create unpublished draft forms |\n| `entries:read` | Read entry data (sensitive fields redacted) |\n| `entries:write` | Create new entries |\n| `webhooks:write` | Create and delete webhooks |\n| `entries:read:sensitive` | Read sensitive/encrypted field values |\n\n## Pagination\n\nList endpoints use cursor-based pagination. The response includes a `pagination` object with:\n- `hasMore`: Whether more results exist\n- `nextCursor`: Cursor to pass for the next page\n\n## Errors\n\nAll errors return a consistent JSON structure:\n\n```json\n{\n  \"error\": {\n    \"code\": \"error_code\",\n    \"message\": \"Human readable message\",\n    \"details\": { }\n  }\n}\n```\n"},"servers":[{"url":"{baseUrl}","variables":{"baseUrl":{"default":"https://grtforms.com","description":"GRTforms production URL"}}}],"tags":[{"name":"Forms","description":"Form management"},{"name":"Entries","description":"Entry (submission) management"},{"name":"Webhooks","description":"Webhook management"},{"name":"Account","description":"Account information"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"API Key","description":"API key in the format: grt_live_<prefix>_<secret>"}},"schemas":{"Form":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"slug":{"type":"string"},"isPublished":{"type":"boolean"},"submissionCount":{"type":"integer"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","description","slug","isPublished","submissionCount","createdAt","updatedAt"]},"Pagination":{"type":"object","properties":{"hasMore":{"type":"boolean"},"nextCursor":{"type":["string","null"]}},"required":["hasMore","nextCursor"]},"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string"},"message":{"type":"string"},"details":{"type":"object","additionalProperties":{}}},"required":["code","message"]}},"required":["error"]},"FormDetail":{"type":"object","properties":{"id":{"type":"string"},"name":{"type":"string"},"description":{"type":["string","null"]},"slug":{"type":"string"},"schema":{"type":"array","items":{}},"settings":{"type":"object","additionalProperties":{}},"isPublished":{"type":"boolean"},"submissionCount":{"type":"integer"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","name","description","slug","schema","settings","isPublished","submissionCount","createdAt","updatedAt"]},"Entry":{"type":"object","properties":{"id":{"type":"string"},"data":{"type":"object","additionalProperties":{}},"metadata":{"type":["object","null"],"additionalProperties":{}},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","data","metadata","createdAt","updatedAt"]},"User":{"type":"object","properties":{"id":{"type":"string"},"email":{"type":"string","format":"email"},"name":{"type":["string","null"]},"createdAt":{"type":"string","format":"date-time"}},"required":["id","email","name","createdAt"]},"ApiKeyInfo":{"type":["object","null"],"properties":{"id":{"type":"string"},"name":{"type":"string"},"scopes":{"type":"array","items":{"type":"string"}},"expiresAt":{"type":["string","null"],"format":"date-time"},"usageCount":{"type":"integer"}},"required":["id","name","scopes","expiresAt","usageCount"]},"Webhook":{"type":"object","properties":{"id":{"type":"string"},"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string","enum":["entry.created","entry.updated","entry.deleted"]}},"isEnabled":{"type":"boolean"},"failureCount":{"type":"integer"},"lastFailureAt":{"type":["string","null"],"format":"date-time"},"createdAt":{"type":"string","format":"date-time"},"updatedAt":{"type":"string","format":"date-time"}},"required":["id","url","events","isEnabled","failureCount","lastFailureAt","createdAt","updatedAt"]},"WebhookWithSecret":{"type":"object","properties":{"id":{"type":"string"},"formId":{"type":"string"},"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string","enum":["entry.created","entry.updated","entry.deleted"]}},"secret":{"type":"string"},"isEnabled":{"type":"boolean"},"createdAt":{"type":"string","format":"date-time"}},"required":["id","formId","url","events","secret","isEnabled","createdAt"]}},"parameters":{}},"paths":{"/api/v1/forms":{"get":{"summary":"List forms","description":"Returns a paginated list of forms owned by the authenticated user.","tags":["Forms"],"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string"},"required":false,"name":"cursor","in":"query"}],"responses":{"200":{"description":"List of forms","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Form"}},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]}}}},"401":{"description":"Unauthorized","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create a form","description":"Creates an unpublished draft form. Requires the `forms:write` scope. Schema is validated with the same form schema validators as the builder. Plan form-count limits are enforced.","tags":["Forms"],"security":[{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"description":{"type":"string"},"schema":{}},"required":["name"]}}}},"responses":{"201":{"description":"Draft form created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/FormDetail"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Missing forms:write or plan form limit","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/forms/{formId}":{"get":{"summary":"Get form","description":"Returns details for a specific form.","tags":["Forms"],"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"formId","in":"path"}],"responses":{"200":{"description":"Form details","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/FormDetail"}},"required":["data"]}}}},"404":{"description":"Form not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/forms/{formId}/entries":{"get":{"summary":"List entries","description":"Returns a paginated list of entries for a form. Sensitive fields are redacted unless the `entries:read:sensitive` scope is provided. Pass `field` and `value` together to find entries whose JSON data matches that field.","tags":["Entries"],"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"formId","in":"path"},{"schema":{"type":"string"},"required":false,"name":"limit","in":"query"},{"schema":{"type":"string"},"required":false,"name":"cursor","in":"query"},{"schema":{"type":"string"},"required":false,"name":"from","in":"query"},{"schema":{"type":"string"},"required":false,"name":"to","in":"query"},{"schema":{"type":"string","enum":["asc","desc"]},"required":false,"name":"sort","in":"query"},{"schema":{"type":"string"},"required":false,"name":"fields","in":"query"},{"schema":{"type":"string"},"required":false,"name":"field","in":"query"},{"schema":{"type":"string"},"required":false,"name":"value","in":"query"}],"responses":{"200":{"description":"List of entries","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Entry"}},"pagination":{"$ref":"#/components/schemas/Pagination"}},"required":["data","pagination"]}}}},"404":{"description":"Form not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create entry","description":"Creates a new entry for a form. Uses the same server-side validation and conditional logic as public submit: hidden and skipped fields are dropped, then remaining required fields are validated. The server stores source: \"api\" on the entry metadata after merging any caller metadata, or source: \"make\" or \"n8n\" when the caller sets those values. Entries marked make or n8n are treated as API-created for consent. Sending true for a Consent field means the API caller is vouching that the person consented to the form's current agreement text; the stored value is stamped with source: \"api\". Mailchimp skips consent on API-created entries (Consent fields and a single-option checkbox used for opt-in) unless Allow API-created consent is on. API key rate limits apply.","tags":["Entries"],"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"formId","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","additionalProperties":{}},"metadata":{"type":"object","additionalProperties":{}}},"required":["data"]}}}},"responses":{"201":{"description":"Entry created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Entry"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Form not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/forms/{formId}/entries/count":{"get":{"summary":"Count entries","description":"Returns the total number of entries for a form, optionally filtered by date range.","tags":["Entries"],"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"formId","in":"path"},{"schema":{"type":"string"},"required":false,"name":"from","in":"query"},{"schema":{"type":"string"},"required":false,"name":"to","in":"query"}],"responses":{"200":{"description":"Entry count","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"count":{"type":"integer"}},"required":["count"]}},"required":["data"]}}}},"404":{"description":"Form not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/forms/{formId}/entries/{entryId}":{"get":{"summary":"Get entry","description":"Returns a single entry by ID.","tags":["Entries"],"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"formId","in":"path"},{"schema":{"type":"string"},"required":true,"name":"entryId","in":"path"}],"responses":{"200":{"description":"Entry details","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/Entry"}},"required":["data"]}}}},"404":{"description":"Entry not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/me":{"get":{"summary":"Get current user","description":"Returns information about the authenticated user and API key.","tags":["Account"],"security":[{"bearerAuth":[]}],"responses":{"200":{"description":"User and API key info","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"user":{"$ref":"#/components/schemas/User"},"apiKey":{"$ref":"#/components/schemas/ApiKeyInfo"}},"required":["user","apiKey"]}},"required":["data"]}}}}}}},"/api/v1/webhooks":{"get":{"summary":"List webhooks","description":"Returns webhooks for a form.","tags":["Webhooks"],"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"formId","in":"query"}],"responses":{"200":{"description":"List of webhooks","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Webhook"}}},"required":["data"]}}}},"404":{"description":"Form not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"summary":"Create webhook","description":"Creates a new webhook for a form. The signing secret is returned only once.","tags":["Webhooks"],"security":[{"bearerAuth":[]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"formId":{"type":"string"},"url":{"type":"string","format":"uri"},"events":{"type":"array","items":{"type":"string","enum":["entry.created","entry.updated","entry.deleted"]}}},"required":["formId","url","events"]}}}},"responses":{"201":{"description":"Webhook created","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"$ref":"#/components/schemas/WebhookWithSecret"}},"required":["data"]}}}},"400":{"description":"Validation error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Form not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/api/v1/webhooks/{webhookId}":{"delete":{"summary":"Delete webhook","description":"Deletes a webhook.","tags":["Webhooks"],"security":[{"bearerAuth":[]}],"parameters":[{"schema":{"type":"string"},"required":true,"name":"webhookId","in":"path"}],"responses":{"200":{"description":"Webhook deleted","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"object","properties":{"success":{"type":"boolean"}},"required":["success"]}},"required":["data"]}}}},"404":{"description":"Webhook not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"webhooks":{}}