{ "openapi": "3.1.0", "info": { "title": "Factify API", "version": "1.0.0", "description": "Factify API enables you to create, manage, and control access to legally-binding documents that replace PDFs.\n\n## Authentication\n\nBearer authentication header of the form Bearer \u003ctoken\u003e, where \u003ctoken\u003e is your auth token.\n\n```\nAuthorization: Bearer ffy...\n```\n\n## Rate Limiting\n\n- 1000 requests per minute per API key\n- 100 requests per minute for resource creation (POST)\n\n## Errors\n\nFactify uses conventional HTTP status codes and returns structured error responses:\n\n```json\n{\n \"error\": {\n \"type\": \"invalid_request_error\",\n \"message\": \"Document title cannot be empty\",\n \"param\": \"title\",\n \"code\": \"missing_required_field\"\n }\n}\n```\n", "contact": { "name": "Factify API Support", "email": "api@factify.com", "url": "https://factify.com/support" }, "license": { "name": "Proprietary", "url": "https://factify.com/terms" } }, "servers": [ { "url": "https://api.factify.com", "description": "Production" }, { "url": "https://api.factify-dev.com", "description": "Development (internal only)" } ], "tags": [ { "name": "Documents", "description": "Create, retrieve, update, and manage documents." }, { "name": "Versions", "description": "Manage document versions and version history." }, { "name": "Organizations", "description": "Create and manage organizations and member invitations." }, { "name": "API Keys", "description": "Generate and manage API keys for authentication." }, { "name": "Policies", "description": "Attach and manage access policies for documents." }, { "name": "Sharing \u0026 Distribution", "description": "Generate shareable entry pages for documents." }, { "name": "DocumentService", "description": "DocumentService provides operations on documents.\n Tags default to \"DocumentService\", post-processor renames to \"Documents\"." }, { "name": "EntryPageService", "description": "EntryPageService provides operations for document entry pages.\n Tags default to \"EntryPageService\", post-processor renames to \"Sharing \u0026 Distribution\"." }, { "name": "IdentityService", "description": "IdentityService provides APIs for managing API keys.\n Enables programmatic access through API key generation and management.\n\n v1beta Scope:\n - Services and service accounts are NOT exposed in v1beta. When an organization\n is created, default service and service account are provisioned internally.\n - API keys grant organization admin privileges. Granular permissions will be\n added in a future version.\n\n Authorization Model:\n - CreateApiKey: Requires organization admin role.\n - ListApiKeys: Requires organization admin role.\n - RevokeApiKey: Requires organization admin role.\n\n Lifecycle Management:\n - API keys can be created and revoked but not updated. Use revoke + create for rotation." }, { "name": "OrganizationService", "description": "OrganizationService provides APIs for managing organizations and member invitations.\n\n Authorization Model:\n - CreateOrganization: Any authenticated user. The caller becomes the organization owner.\n Specifying owner_user_id for a different user requires system maintainer role.\n - GetOrganization/ListOrganizations: Requires organization membership.\n - Invitation operations: Requires organization#manage permission.\n\n Lifecycle Management:\n - Organizations are immutable after creation (no update/delete).\n - Invitations can be created, revoked, and resent. They expire after 7 days. ==========================================================================\n Organizations\n ==========================================================================" }, { "name": "PolicyService", "description": "PolicyService provides operations on document policies.\n Tags default to \"PolicyService\", post-processor renames to \"Policies\"." }, { "name": "VersionService", "description": "VersionService provides operations on document versions.\n Tags default to \"VersionService\", post-processor renames to \"Versions\"." } ], "security": [ { "bearerAuth": [] } ], "components": { "securitySchemes": { "bearerAuth": { "type": "http", "scheme": "bearer", "bearerFormat": "API Key", "description": "Bearer authentication header of the form Bearer \u003ctoken\u003e, where \u003ctoken\u003e is your auth token." } }, "responses": { "BadRequest": { "description": "Invalid request parameters", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "examples": { "validation_error": { "value": { "error": { "type": "invalid_request_error", "message": "Document title cannot be empty", "code": "missing_required_field", "param": "title", "retry_after": null } } } } } } }, "Forbidden": { "description": "Authorization failed - valid key but insufficient permissions", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "examples": { "insufficient_permissions": { "value": { "error": { "type": "authorization_error", "message": "You do not have permission to access this resource", "code": "insufficient_permissions", "param": null, "retry_after": null } } } } } } }, "InternalServerError": { "description": "Internal server error", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "examples": { "internal_error": { "value": { "error": { "type": "api_error", "message": "An unexpected error occurred. Please try again later.", "code": "internal_error", "param": null, "retry_after": null } } } } } } }, "NotFound": { "description": "Resource not found", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "examples": { "resource_not_found": { "value": { "error": { "type": "not_found_error", "message": "Document not found", "code": "resource_not_found", "param": null, "retry_after": null } } } } } } }, "RateLimitExceeded": { "description": "Too many requests", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "examples": { "rate_limit": { "value": { "error": { "type": "rate_limit_error", "message": "Too many requests. Retry after 30 seconds.", "code": "rate_limit_exceeded", "param": null, "retry_after": 30 } } } } } }, "headers": { "X-RateLimit-Limit": { "schema": { "type": "integer" }, "description": "Request limit per minute" }, "X-RateLimit-Remaining": { "schema": { "type": "integer" }, "description": "Remaining requests in current window" }, "X-RateLimit-Reset": { "schema": { "type": "integer" }, "description": "Unix timestamp when rate limit resets" } } }, "Unauthorized": { "description": "Authentication failed - missing or invalid API key", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "examples": { "missing_key": { "value": { "error": { "type": "authentication_error", "message": "No API key provided", "code": "missing_api_key", "param": null, "retry_after": null } } } } } } } }, "schemas": { "Error": { "type": "object", "required": [ "error" ], "properties": { "error": { "type": "object", "required": [ "type", "message", "code", "param", "retry_after" ], "properties": { "type": { "type": "string", "enum": [ "invalid_request_error", "authentication_error", "authorization_error", "not_found_error", "rate_limit_error", "api_error" ], "description": "Error type for programmatic handling" }, "message": { "type": "string", "description": "Human-readable error message" }, "code": { "type": "string", "description": "Specific error code for detailed handling" }, "param": { "type": [ "string", "null" ], "description": "Parameter that caused the error (for validation errors)" }, "retry_after": { "type": [ "integer", "null" ], "description": "Seconds to wait before retrying (for rate limit errors)" } } } } }, "factify.api.v1beta.AcceptOrganizationInviteRequest": { "type": "object", "properties": { "organization_id": { "type": "string", "title": "organization_id", "pattern": "^org_[0-9a-hjkmnp-tv-z]{26}$", "description": "Organization the invitation belongs to (for validation).\n Pattern: org_[0-9a-hjkmnp-tv-z]{26}" }, "token": { "type": "string", "title": "token", "maxLength": 128, "description": "The invitation token from the email link." } }, "title": "AcceptOrganizationInviteRequest", "required": [ "organization_id", "token" ], "additionalProperties": false, "description": "AcceptOrganizationInviteRequest contains the token to accept an invitation." }, "factify.api.v1beta.AcceptOrganizationInviteResponse": { "type": "object", "properties": { "organization": { "title": "organization", "description": "The organization the user is now a member of.", "$ref": "#/components/schemas/factify.api.v1beta.Organization" } }, "title": "AcceptOrganizationInviteResponse", "required": [ "organization" ], "additionalProperties": false, "description": "AcceptOrganizationInviteResponse contains the organization the user joined." }, "factify.api.v1beta.AccessLevel": { "enum": [ "private", "organization", "public" ] }, "factify.api.v1beta.ApiKey": { "type": "object", "properties": { "id": { "type": "string", "examples": [ "key_01jd4h5mck9gq6zrp8bn2t4w3x" ], "title": "id", "pattern": "^key_[0-9a-hjkmnp-tv-z]{26}$", "description": "Unique ID for the API key.\n Pattern: key_[0-9a-hjkmnp-tv-z]{26}" }, "name": { "type": "string", "examples": [ "Production" ], "title": "name", "maxLength": 63, "minLength": 1, "description": "Human-readable name for the key (e.g., \"Production\", \"Staging\")." }, "organization_id": { "type": "string", "examples": [ "org_01h2xcejqtf2nbrexx3vqjhp41" ], "title": "organization_id", "pattern": "^org_[0-9a-hjkmnp-tv-z]{26}$", "description": "ID of the organization this key belongs to.\n Pattern: org_[0-9a-hjkmnp-tv-z]{26}" }, "created_at": { "title": "created_at", "description": "Timestamp when the key was created.", "$ref": "#/components/schemas/google.protobuf.Timestamp" }, "expires_at": { "title": "expires_at", "description": "Expiration timestamp. If set, the key becomes invalid after this time.", "$ref": "#/components/schemas/google.protobuf.Timestamp" }, "is_active": { "type": "boolean", "title": "is_active", "description": "Whether the key is currently active (not revoked and not expired).\n Computed: is_active = (revoked_at == null) \u0026\u0026 (expires_at == null || expires_at \u003e now())", "readOnly": true }, "key_prefix": { "type": "string", "examples": [ "ffy_prod_01jd4h5..." ], "title": "key_prefix", "maxLength": 20, "minLength": 16, "description": "Truncated key prefix for identification in UI (e.g., \"ffy_prod_01jd4h5...\").\n Shows the first 16 characters for better uniqueness while maintaining security." }, "revoked_at": { "title": "revoked_at", "description": "Timestamp when the key was revoked. Null if the key has not been revoked.", "readOnly": true, "$ref": "#/components/schemas/google.protobuf.Timestamp" } }, "title": "ApiKey", "required": [ "id", "name", "organization_id", "created_at", "is_active", "key_prefix" ], "additionalProperties": false, "description": "ApiKey represents an authentication credential for programmatic API access." }, "factify.api.v1beta.AttachPolicyRequest": { "type": "object", "properties": { "document_id": { "type": "string", "title": "document_id", "pattern": "^doc_[0-9a-hjkmnp-tv-z]{26}$", "description": "Document ID.\n Pattern: doc_[0-9a-hjkmnp-tv-z]{26}" }, "policy_id": { "type": "string", "title": "policy_id", "pattern": "^pol_[0-9a-hjkmnp-tv-z]{26}$", "description": "Policy ID to attach.\n Pattern: pol_[0-9a-hjkmnp-tv-z]{26}" } }, "title": "AttachPolicyRequest", "required": [ "policy_id" ], "additionalProperties": false, "description": "AttachPolicyRequest specifies a policy to attach to a document." }, "factify.api.v1beta.CreateApiKeyRequest": { "type": "object", "properties": { "organization_id": { "type": "string", "title": "organization_id", "pattern": "^org_[0-9a-hjkmnp-tv-z]{26}$", "description": "Organization ID to create the key for.\n Pattern: org_[0-9a-hjkmnp-tv-z]{26}" }, "name": { "type": "string", "title": "name", "maxLength": 63, "minLength": 1, "description": "Human-readable name for the key (e.g., \"Production\", \"Staging\")." }, "expires_at": { "title": "expires_at", "description": "Expiration timestamp. If not set, the key does not expire.\n When set, must be in the future and within 1 year.\n Security note: Keys without expiration should be rotated periodically.\ntimestamp.gt_now = true\ntimestamp.within = 8760h0m0s\n", "$ref": "#/components/schemas/google.protobuf.Timestamp" } }, "title": "CreateApiKeyRequest", "required": [ "organization_id", "name" ], "additionalProperties": false, "description": "CreateApiKeyRequest contains the data needed to create a new API key." }, "factify.api.v1beta.CreateApiKeyResponse": { "type": "object", "properties": { "api_key": { "title": "api_key", "description": "The created API key metadata.", "$ref": "#/components/schemas/factify.api.v1beta.ApiKey" }, "secret": { "type": "string", "examples": [ "ffy_prod_01h2xcejqtf2nbrexx3vqjhp41KJ8f3mNpQrStUvWxYz0123456789ABCDEFGHIJKLmNo" ], "title": "secret", "minLength": 78, "pattern": "^ffy_(prod|test)_[A-Za-z0-9]+$", "description": "The API key secret. This is only returned once and cannot be retrieved later.\n Format: ffy_{env}_{base32_uuid7}{base62_random}\n - env: \"prod\" or \"test\"\n - base32_uuid7: 26 chars (TypeID base32 alphabet: 0-9a-hjkmnp-tv-z)\n - base62_random: 43 chars (0-9A-Za-z)\n Store this value securely - it will not be shown again.\n SECURITY WARNING: This field must NEVER be logged. Handlers must redact secrets from all logs." } }, "title": "CreateApiKeyResponse", "required": [ "api_key", "secret" ], "additionalProperties": false, "description": "CreateApiKeyResponse contains the newly created API key and its secret." }, "factify.api.v1beta.CreateDocumentRequest": { "type": "object", "properties": { "payload": { "type": "string", "title": "payload", "format": "binary", "description": "PDF file to upload" }, "title": { "type": "string", "title": "title", "maxLength": 255, "minLength": 1, "description": "Document title." }, "description": { "type": "string", "title": "description", "maxLength": 2000, "description": "Optional document description.", "nullable": true }, "access_level": { "title": "access_level", "description": "Document access level. Defaults to organization if user belongs to an org, otherwise private.", "nullable": true, "$ref": "#/components/schemas/factify.api.v1beta.AccessLevel" }, "payload_key": { "type": "string", "title": "payload_key", "description": "Storage key for uploaded file (set by server)", "readOnly": true }, "document_id": { "type": "string", "title": "document_id", "description": "Pre-generated document ID (set by server)", "readOnly": true }, "version_id": { "type": "string", "title": "version_id", "description": "Pre-generated version ID (set by server)", "readOnly": true } }, "title": "CreateDocumentRequest", "required": [ "payload", "title" ], "additionalProperties": false, "description": "CreateDocumentRequest contains the file and metadata for creating a document." }, "factify.api.v1beta.CreateDocumentResponse": { "type": "object", "properties": { "document": { "title": "document", "description": "The created document.", "$ref": "#/components/schemas/factify.api.v1beta.Document" } }, "title": "CreateDocumentResponse", "required": [ "document" ], "additionalProperties": false, "description": "CreateDocumentResponse contains the newly created document." }, "factify.api.v1beta.CreateOrganizationInviteRequest": { "type": "object", "properties": { "organization_id": { "type": "string", "title": "organization_id", "pattern": "^org_[0-9a-hjkmnp-tv-z]{26}$", "description": "Organization to invite the user to.\n Pattern: org_[0-9a-hjkmnp-tv-z]{26}" }, "email": { "type": "string", "title": "email", "format": "email", "description": "Email address of the recipient." }, "message": { "type": "string", "title": "message", "maxLength": 2000, "description": "Optional custom message from the sender (max 2000 bytes to support ~500 multibyte characters).", "nullable": true }, "idempotency_key": { "type": "string", "title": "idempotency_key", "maxLength": 255, "description": "Reserved for future use. Currently, idempotency is based on email matching:\n if a PENDING invitation exists for the same email in this organization,\n that invitation is resent rather than creating a duplicate.", "nullable": true } }, "title": "CreateOrganizationInviteRequest", "required": [ "organization_id", "email" ], "additionalProperties": false, "description": "CreateOrganizationInviteRequest contains the data needed to create an invitation." }, "factify.api.v1beta.CreateOrganizationInviteResponse": { "type": "object", "properties": { "invite": { "title": "invite", "description": "The created (or existing) invitation.", "$ref": "#/components/schemas/factify.api.v1beta.OrganizationInvite" } }, "title": "CreateOrganizationInviteResponse", "required": [ "invite" ], "additionalProperties": false, "description": "CreateOrganizationInviteResponse contains the created invitation." }, "factify.api.v1beta.CreateOrganizationRequest": { "type": "object", "properties": { "name": { "type": "string", "title": "name", "maxLength": 255, "minLength": 1, "description": "Human-readable organization name." }, "owner_user_id": { "type": "string", "title": "owner_user_id", "pattern": "^user_[0-9a-hjkmnp-tv-z]{26}$", "description": "Owner user ID. If not provided, the authenticated user becomes the owner.\n Authorization: Specifying a different user requires system maintainer role.\n Required when the caller is a service account (service accounts cannot own organizations).\n Pattern: user_[0-9a-hjkmnp-tv-z]{26}", "nullable": true } }, "title": "CreateOrganizationRequest", "required": [ "name" ], "additionalProperties": false, "description": "CreateOrganizationRequest contains the data needed to create a new organization." }, "factify.api.v1beta.CreateOrganizationResponse": { "type": "object", "properties": { "organization": { "title": "organization", "description": "The created organization.", "$ref": "#/components/schemas/factify.api.v1beta.Organization" } }, "title": "CreateOrganizationResponse", "required": [ "organization" ], "additionalProperties": false, "description": "CreateOrganizationResponse contains the newly created organization." }, "factify.api.v1beta.CreateVersionRequest": { "type": "object", "properties": { "document_id": { "type": "string", "title": "document_id", "pattern": "^doc_[0-9a-hjkmnp-tv-z]{26}$", "description": "Document ID to create version for.\n Pattern: doc_[0-9a-hjkmnp-tv-z]{26}" }, "payload": { "type": "string", "title": "payload", "format": "binary", "description": "PDF file for new version" }, "title": { "type": "string", "title": "title", "maxLength": 255, "description": "Optional version title.", "nullable": true }, "description": { "type": "string", "title": "description", "description": "Description of changes in this version.", "nullable": true }, "payload_key": { "type": "string", "title": "payload_key", "description": "Storage key for uploaded file (set by server)", "readOnly": true }, "version_id": { "type": "string", "title": "version_id", "description": "Pre-generated version ID (set by server)", "readOnly": true } }, "title": "CreateVersionRequest", "required": [ "payload" ], "additionalProperties": false, "description": "CreateVersionRequest contains the file and metadata for creating a version." }, "factify.api.v1beta.DetachPolicyRequest": { "type": "object", "properties": { "document_id": { "type": "string", "title": "document_id", "pattern": "^doc_[0-9a-hjkmnp-tv-z]{26}$", "description": "Document ID.\n Pattern: doc_[0-9a-hjkmnp-tv-z]{26}" }, "policy_id": { "type": "string", "title": "policy_id", "pattern": "^pol_[0-9a-hjkmnp-tv-z]{26}$", "description": "Policy ID to detach.\n Pattern: pol_[0-9a-hjkmnp-tv-z]{26}" } }, "title": "DetachPolicyRequest", "additionalProperties": false, "description": "DetachPolicyRequest identifies a policy to detach from a document." }, "factify.api.v1beta.Document": { "type": "object", "properties": { "id": { "type": "string", "examples": [ "doc_01h2xcejqtf2nbrexx3vqjhp41" ], "title": "id", "pattern": "^doc_[0-9a-hjkmnp-tv-z]{26}$", "description": "Unique ID for the document.\n Pattern: doc_[0-9a-hjkmnp-tv-z]{26}" }, "url": { "type": "string", "examples": [ "https://d.factify.com/d/01h2xcejqtf2nbrexx3vqjhp41" ], "title": "url", "format": "uri", "description": "URL for accessing the document on Factify." }, "title": { "type": "string", "examples": [ "Q4 2024 Financial Report" ], "title": "title", "maxLength": 255, "minLength": 1, "description": "Document title." }, "description": { "type": "string", "examples": [ "Quarterly financial report for Q4 2024" ], "title": "description", "maxLength": 2000, "description": "Optional document description.", "nullable": true }, "access_level": { "title": "access_level", "description": "Document access level.", "$ref": "#/components/schemas/factify.api.v1beta.AccessLevel" }, "processing_status": { "title": "processing_status", "description": "Processing status.", "$ref": "#/components/schemas/factify.api.v1beta.ProcessingStatus" }, "created_at": { "title": "created_at", "description": "Timestamp when document was created.", "$ref": "#/components/schemas/google.protobuf.Timestamp" }, "created_by": { "title": "created_by", "description": "User who created the document.", "$ref": "#/components/schemas/factify.api.v1beta.User" }, "current_version": { "title": "current_version", "description": "Reference to the current version, if any.", "nullable": true, "$ref": "#/components/schemas/factify.api.v1beta.VersionRef" } }, "title": "Document", "required": [ "id", "url", "title", "access_level", "processing_status", "created_at", "created_by" ], "additionalProperties": false, "description": "Document represents a Factify document." }, "factify.api.v1beta.DocumentPolicy": { "type": "object", "properties": { "document_id": { "type": "string", "examples": [ "doc_01h2xcejqtf2nbrexx3vqjhp41" ], "title": "document_id", "pattern": "^doc_[0-9a-hjkmnp-tv-z]{26}$", "description": "Document ID this policy is attached to.\n Pattern: doc_[0-9a-hjkmnp-tv-z]{26}" }, "attached_at": { "title": "attached_at", "description": "Timestamp when policy was attached.", "$ref": "#/components/schemas/google.protobuf.Timestamp" }, "attached_by": { "title": "attached_by", "description": "User who attached the policy.", "$ref": "#/components/schemas/factify.api.v1beta.User" }, "policy": { "title": "policy", "description": "The policy details.", "$ref": "#/components/schemas/factify.api.v1beta.Policy" } }, "title": "DocumentPolicy", "required": [ "document_id", "attached_at", "attached_by", "policy" ], "additionalProperties": false, "description": "DocumentPolicy represents a policy attached to a document." }, "factify.api.v1beta.GenerateEntryPageRequest": { "type": "object", "properties": { "document_id": { "type": "string", "title": "document_id", "pattern": "^doc_[0-9a-hjkmnp-tv-z]{26}$", "description": "Document ID.\n Pattern: doc_[0-9a-hjkmnp-tv-z]{26}" } }, "title": "GenerateEntryPageRequest", "additionalProperties": false, "description": "GenerateEntryPageRequest identifies a document to generate an entry page for." }, "factify.api.v1beta.GenerateEntryPageResponse": { "type": "object", "properties": { "download_url": { "type": "string", "examples": [ "https://storage.factify.com/entry-pages/abc123.pdf?token=xyz" ], "title": "download_url", "format": "uri", "description": "Temporary download URL for the entry page PDF." } }, "title": "GenerateEntryPageResponse", "required": [ "download_url" ], "additionalProperties": false, "description": "GenerateEntryPageResponse contains the download URL for the entry page." }, "factify.api.v1beta.GetDocumentRequest": { "type": "object", "properties": { "document_id": { "type": "string", "title": "document_id", "pattern": "^doc_[0-9a-hjkmnp-tv-z]{26}$", "description": "Document ID.\n Pattern: doc_[0-9a-hjkmnp-tv-z]{26}" } }, "title": "GetDocumentRequest", "additionalProperties": false, "description": "GetDocumentRequest identifies a document to retrieve." }, "factify.api.v1beta.GetDocumentResponse": { "type": "object", "properties": { "document": { "title": "document", "description": "The requested document.", "$ref": "#/components/schemas/factify.api.v1beta.Document" } }, "title": "GetDocumentResponse", "required": [ "document" ], "additionalProperties": false, "description": "GetDocumentResponse contains the requested document." }, "factify.api.v1beta.GetOrganizationRequest": { "type": "object", "properties": { "organization_id": { "type": "string", "title": "organization_id", "pattern": "^org_[0-9a-hjkmnp-tv-z]{26}$", "description": "Organization ID.\n Pattern: org_[0-9a-hjkmnp-tv-z]{26}" } }, "title": "GetOrganizationRequest", "required": [ "organization_id" ], "additionalProperties": false, "description": "GetOrganizationRequest identifies an organization to retrieve." }, "factify.api.v1beta.GetOrganizationResponse": { "type": "object", "properties": { "organization": { "title": "organization", "description": "The requested organization.", "$ref": "#/components/schemas/factify.api.v1beta.Organization" } }, "title": "GetOrganizationResponse", "required": [ "organization" ], "additionalProperties": false, "description": "GetOrganizationResponse contains the requested organization." }, "factify.api.v1beta.GetVersionRequest": { "type": "object", "properties": { "version_id": { "type": "string", "title": "version_id", "pattern": "^ver_[0-9a-hjkmnp-tv-z]{26}$", "description": "Version ID to retrieve.\n Pattern: ver_[0-9a-hjkmnp-tv-z]{26}" } }, "title": "GetVersionRequest", "additionalProperties": false, "description": "GetVersionRequest identifies a specific version to retrieve." }, "factify.api.v1beta.ListApiKeysRequest": { "type": "object", "properties": { "organization_id": { "type": "string", "title": "organization_id", "pattern": "^org_[0-9a-hjkmnp-tv-z]{26}$", "description": "Organization ID to list keys for.\n Pattern: org_[0-9a-hjkmnp-tv-z]{26}" }, "include_revoked": { "type": "boolean", "title": "include_revoked", "description": "Whether to include revoked keys in the response.\n Default: false (only active and expired-but-not-revoked keys are returned).\n When true, all keys including revoked ones are returned.\n Note: Expired keys are always included unless also revoked; use is_active to filter." }, "page_token": { "type": "string", "title": "page_token", "description": "Opaque pagination token from a previous response." }, "page_size": { "type": "integer", "title": "page_size", "maximum": 100, "minimum": 1, "format": "int32", "description": "Maximum number of items to return per page (1-100). Default: 50.", "nullable": true } }, "title": "ListApiKeysRequest", "required": [ "organization_id" ], "additionalProperties": false, "description": "ListApiKeysRequest specifies filters and pagination for listing API keys." }, "factify.api.v1beta.ListApiKeysResponse": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/factify.api.v1beta.ApiKey" }, "title": "items", "description": "List of API keys (secrets are never included)." }, "pagination": { "title": "pagination", "description": "Pagination metadata.", "$ref": "#/components/schemas/factify.api.v1beta.Pagination" } }, "title": "ListApiKeysResponse", "required": [ "pagination" ], "additionalProperties": false, "description": "ListApiKeysResponse contains a page of API keys." }, "factify.api.v1beta.ListDocumentPoliciesRequest": { "type": "object", "properties": { "document_id": { "type": "string", "title": "document_id", "pattern": "^doc_[0-9a-hjkmnp-tv-z]{26}$", "description": "Document ID.\n Pattern: doc_[0-9a-hjkmnp-tv-z]{26}" }, "page_token": { "type": "string", "examples": [ "eyJpZCI6InBvbF8wMWgyeGNlanF0ZjJuYnJleHgzdnFqaHA0MSIsImQiOiJuZXh0IiwiZiI6eyJkb2NfaWQiOiJkb2NfMDFoMnhjZWpxdGYybmJyZXh4M3ZxamhwNDEifX0" ], "title": "page_token", "description": "Opaque pagination token from a previous response.\n Pass next_page_token or prev_page_token from a previous response to continue pagination.\n Empty or omitted for the first page." }, "page_size": { "type": "integer", "title": "page_size", "maximum": 100, "minimum": 1, "format": "int32", "description": "Maximum number of items to return per page (1-100). Default: 50.", "nullable": true } }, "title": "ListDocumentPoliciesRequest", "additionalProperties": false, "description": "ListDocumentPoliciesRequest specifies pagination for listing document policies.\n Follows Google AIP-158 for pagination field naming." }, "factify.api.v1beta.ListDocumentPoliciesResponse": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/factify.api.v1beta.DocumentPolicy" }, "title": "items", "description": "List of policy attachments." }, "pagination": { "title": "pagination", "description": "Pagination metadata.", "$ref": "#/components/schemas/factify.api.v1beta.Pagination" } }, "title": "ListDocumentPoliciesResponse", "required": [ "pagination" ], "additionalProperties": false, "description": "ListDocumentPoliciesResponse contains a page of policy attachments." }, "factify.api.v1beta.ListDocumentsRequest": { "type": "object", "properties": { "page_token": { "type": "string", "examples": [ "eyJpZCI6ImRvY18wMWgyeGNlanF0ZjJuYnJleHgzdnFqaHA0MSIsImQiOiJuZXh0In0" ], "title": "page_token", "description": "Opaque pagination token from a previous response.\n Pass next_page_token or prev_page_token from a previous response to continue pagination.\n Empty or omitted for the first page." }, "page_size": { "type": "integer", "title": "page_size", "maximum": 100, "minimum": 1, "format": "int32", "description": "Maximum number of items to return per page (1-100). Default: 50.", "nullable": true }, "created": { "title": "created", "description": "Filter by creation time range.\n REST: ?created.after=2024-01-01T00:00:00Z\u0026created.before=2024-12-31T23:59:59Z", "nullable": true, "$ref": "#/components/schemas/factify.api.v1beta.TimestampFilter" }, "created_by_id": { "type": "string", "title": "created_by_id", "description": "Filter by creator ID(s) (user or service account). Returns documents matching ANY of the specified IDs.\n Accepts single value or comma-separated list.\n REST: ?created_by_id=user_01h2xcejqtf2nbrexx3vqjhp41 or ?created_by_id=user_xxx,svc_yyy", "nullable": true }, "access_level": { "type": "string", "title": "access_level", "description": "Filter by access level(s). Returns documents matching ANY of the specified levels.\n Accepts single value or comma-separated list.\n REST: ?access_level=private or ?access_level=private,organization\n Accepts clean enum names (case-insensitive): \"private\", \"organization\", \"public\"", "nullable": true }, "processing_status": { "type": "string", "title": "processing_status", "description": "Filter by processing status(es). Returns documents matching ANY of the specified statuses.\n Accepts single value or comma-separated list.\n REST: ?processing_status=ready or ?processing_status=processing,ready,failed\n Accepts clean enum names (case-insensitive): \"processing\", \"ready\", \"failed\"", "nullable": true } }, "title": "ListDocumentsRequest", "additionalProperties": false, "description": "ListDocumentsRequest specifies filters and pagination for listing documents.\n Follows Google AIP-158 for pagination field naming.\n Uses Vanguard-compatible dot notation for nested filters: ?created.after=...\u0026created.before=...\ncreated_time_range // created.after must be before created.before\n" }, "factify.api.v1beta.ListDocumentsResponse": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/factify.api.v1beta.Document" }, "title": "items", "description": "List of documents." }, "pagination": { "title": "pagination", "description": "Pagination metadata.", "$ref": "#/components/schemas/factify.api.v1beta.Pagination" } }, "title": "ListDocumentsResponse", "required": [ "pagination" ], "additionalProperties": false, "description": "ListDocumentsResponse contains a page of documents." }, "factify.api.v1beta.ListOrganizationInvitesRequest": { "type": "object", "properties": { "organization_id": { "type": "string", "title": "organization_id", "pattern": "^org_[0-9a-hjkmnp-tv-z]{26}$", "description": "Organization to list invitations for.\n Pattern: org_[0-9a-hjkmnp-tv-z]{26}" }, "status": { "type": "array", "items": { "$ref": "#/components/schemas/factify.api.v1beta.OrganizationInviteStatus" }, "title": "status", "description": "Filter by status. If empty, returns all invitations.\n ORGANIZATION_INVITE_STATUS_UNSPECIFIED (0) is not a valid filter value." }, "page_token": { "type": "string", "title": "page_token", "description": "Opaque pagination token from a previous response." }, "page_size": { "type": "integer", "title": "page_size", "maximum": 100, "minimum": 1, "format": "int32", "description": "Maximum number of items to return per page (1-100). Default: 50.", "nullable": true }, "email": { "title": "email", "description": "Filter by email address.\n REST: ?email.contains=dwight or ?email.exact=dwight@dundermifflin.com", "nullable": true, "$ref": "#/components/schemas/factify.api.v1beta.StringFilter" }, "sender_id": { "type": "string", "title": "sender_id", "pattern": "^user_[0-9a-hjkmnp-tv-z]{26}$", "description": "Filter by sender. Only returns invitations sent by this user.\n REST: ?sender_id=user_01h2xcejqtf2nbrexx3vqjhp41\n Pattern: user_[0-9a-hjkmnp-tv-z]{26}", "nullable": true }, "created": { "title": "created", "description": "Filter by creation time range.\n REST: ?created.after=2024-01-01T00:00:00Z\u0026created.before=2024-12-31T23:59:59Z", "nullable": true, "$ref": "#/components/schemas/factify.api.v1beta.TimestampFilter" }, "expires": { "title": "expires", "description": "Filter by expiration time range.\n Useful for finding invitations expiring soon (expires.before) or still valid beyond a date (expires.after).\n REST: ?expires.after=2024-01-01T00:00:00Z\u0026expires.before=2024-01-31T23:59:59Z", "nullable": true, "$ref": "#/components/schemas/factify.api.v1beta.TimestampFilter" } }, "title": "ListOrganizationInvitesRequest", "required": [ "organization_id" ], "additionalProperties": false, "description": "ListOrganizationInvitesRequest specifies filters for listing invitations.\ncreated_time_range // created.after must be before created.before\nexpires_time_range // expires.after must be before expires.before\n" }, "factify.api.v1beta.ListOrganizationInvitesResponse": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/factify.api.v1beta.OrganizationInvite" }, "title": "items", "description": "List of invitations." }, "pagination": { "title": "pagination", "description": "Pagination metadata.", "$ref": "#/components/schemas/factify.api.v1beta.Pagination" } }, "title": "ListOrganizationInvitesResponse", "required": [ "pagination" ], "additionalProperties": false, "description": "ListOrganizationInvitesResponse contains a page of invitations." }, "factify.api.v1beta.ListOrganizationsRequest": { "type": "object", "properties": { "page_token": { "type": "string", "title": "page_token", "description": "Opaque pagination token from a previous response." }, "page_size": { "type": "integer", "title": "page_size", "maximum": 100, "minimum": 1, "format": "int32", "description": "Maximum number of items to return per page (1-100). Default: 50.", "nullable": true } }, "title": "ListOrganizationsRequest", "additionalProperties": false, "description": "ListOrganizationsRequest specifies pagination for listing organizations." }, "factify.api.v1beta.ListOrganizationsResponse": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/factify.api.v1beta.Organization" }, "title": "items", "description": "List of organizations." }, "pagination": { "title": "pagination", "description": "Pagination metadata.", "$ref": "#/components/schemas/factify.api.v1beta.Pagination" } }, "title": "ListOrganizationsResponse", "required": [ "pagination" ], "additionalProperties": false, "description": "ListOrganizationsResponse contains a page of organizations." }, "factify.api.v1beta.ListVersionsRequest": { "type": "object", "properties": { "document_id": { "type": "string", "title": "document_id", "pattern": "^doc_[0-9a-hjkmnp-tv-z]{26}$", "description": "Document ID to list versions for.\n Pattern: doc_[0-9a-hjkmnp-tv-z]{26}" }, "page_token": { "type": "string", "examples": [ "eyJpZCI6InZlcl8wMWgyYWJjZDEyMzRlZmdoNTY3OGprbW5wdCIsImQiOiJuZXh0IiwiZiI6eyJkb2NfaWQiOiJkb2NfMDFoMnhjZWpxdGYybmJyZXh4M3ZxamhwNDEifX0" ], "title": "page_token", "description": "Opaque pagination token from a previous response.\n Pass next_page_token or prev_page_token from a previous response to continue pagination.\n Empty or omitted for the first page." }, "page_size": { "type": "integer", "title": "page_size", "maximum": 100, "minimum": 1, "format": "int32", "description": "Maximum number of items to return per page (1-100). Default: 50.", "nullable": true } }, "title": "ListVersionsRequest", "additionalProperties": false, "description": "ListVersionsRequest specifies filters and pagination for listing versions.\n Follows Google AIP-158 for pagination field naming." }, "factify.api.v1beta.ListVersionsResponse": { "type": "object", "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/factify.api.v1beta.Version" }, "title": "items", "description": "List of versions." }, "pagination": { "title": "pagination", "description": "Pagination metadata.", "$ref": "#/components/schemas/factify.api.v1beta.Pagination" } }, "title": "ListVersionsResponse", "required": [ "pagination" ], "additionalProperties": false, "description": "ListVersionsResponse contains a page of versions." }, "factify.api.v1beta.Organization": { "type": "object", "properties": { "id": { "type": "string", "examples": [ "org_01h2xcejqtf2nbrexx3vqjhp41" ], "title": "id", "pattern": "^org_[0-9a-hjkmnp-tv-z]{26}$", "description": "Unique ID for the organization.\n Pattern: org_[0-9a-hjkmnp-tv-z]{26}" }, "name": { "type": "string", "examples": [ "Acme Corporation" ], "title": "name", "maxLength": 255, "minLength": 1, "description": "Human-readable organization name." }, "created_at": { "title": "created_at", "description": "Timestamp when the organization was created.", "$ref": "#/components/schemas/google.protobuf.Timestamp" } }, "title": "Organization", "required": [ "id", "name", "created_at" ], "additionalProperties": false, "description": "Organization represents a Factify organization." }, "factify.api.v1beta.OrganizationInvite": { "type": "object", "properties": { "id": { "type": "string", "examples": [ "inv_01h2xcejqtf2nbrexx3vqjhp41" ], "title": "id", "pattern": "^inv_[0-9a-hjkmnp-tv-z]{26}$", "description": "Unique identifier for the invitation." }, "organization_id": { "type": "string", "examples": [ "org_01h2xcejqtf2nbrexx3vqjhp41" ], "title": "organization_id", "pattern": "^org_[0-9a-hjkmnp-tv-z]{26}$", "description": "Organization the invitation is for." }, "email": { "type": "string", "examples": [ "dwight@dundermifflin.com" ], "title": "email", "format": "email", "description": "Email address of the recipient." }, "status": { "title": "status", "description": "Current status of the invitation.", "$ref": "#/components/schemas/factify.api.v1beta.OrganizationInviteStatus" }, "sender": { "title": "sender", "description": "User who sent the invitation (point-in-time snapshot at creation).", "$ref": "#/components/schemas/factify.api.v1beta.User" }, "created_at": { "title": "created_at", "description": "When the invitation was created.", "$ref": "#/components/schemas/google.protobuf.Timestamp" }, "expires_at": { "title": "expires_at", "description": "When the invitation expires.", "$ref": "#/components/schemas/google.protobuf.Timestamp" }, "accepted_at": { "title": "accepted_at", "description": "When the invitation was accepted (if accepted).", "$ref": "#/components/schemas/google.protobuf.Timestamp" }, "accepted_by": { "title": "accepted_by", "description": "User who accepted the invitation (if accepted).", "$ref": "#/components/schemas/factify.api.v1beta.User" }, "message": { "type": "string", "title": "message", "maxLength": 2000, "description": "Custom message from sender to recipient (if provided).\n Max 2000 bytes to support ~500 multibyte characters.", "nullable": true } }, "title": "OrganizationInvite", "required": [ "id", "organization_id", "email", "status", "sender", "created_at", "expires_at" ], "additionalProperties": false, "description": "OrganizationInvite represents an invitation to join an organization.\naccepted_fields_consistency // accepted_at and accepted_by must both be set or both be unset\n" }, "factify.api.v1beta.OrganizationInviteStatus": { "type": "string", "title": "OrganizationInviteStatus", "enum": [ "ORGANIZATION_INVITE_STATUS_UNSPECIFIED", "ORGANIZATION_INVITE_STATUS_PENDING", "ORGANIZATION_INVITE_STATUS_ACCEPTED", "ORGANIZATION_INVITE_STATUS_EXPIRED", "ORGANIZATION_INVITE_STATUS_REVOKED" ], "description": "OrganizationInviteStatus represents the state of an organization invitation." }, "factify.api.v1beta.Pagination": { "type": "object", "properties": { "next_page_token": { "type": "string", "examples": [ "eyJpZCI6ImRvY18wMWgyeGNlanF0ZjJuYnJleHgzdnFqaHA0MSIsImQiOiJuZXh0In0" ], "title": "next_page_token", "description": "Token to retrieve the next page of results.\n Empty if there are no more results in the forward direction." }, "prev_page_token": { "type": "string", "examples": [ "eyJpZCI6ImRvY18wMWgyeGNlanF0ZjJuYnJleHgzdnFqaHA0MSIsImQiOiJwcmV2In0" ], "title": "prev_page_token", "description": "Token to retrieve the previous page of results.\n Empty if this is the first page." }, "has_more": { "type": "boolean", "title": "has_more", "description": "Whether there are more results in the forward direction." } }, "title": "Pagination", "additionalProperties": false, "description": "Pagination contains cursor-based pagination metadata.\n Follows Google AIP-158 for pagination field naming." }, "factify.api.v1beta.Policy": { "type": "object", "properties": { "id": { "type": "string", "examples": [ "pol_01h2xcejqtf2nbrexx3vqjhp41" ], "title": "id", "pattern": "^pol_[0-9a-hjkmnp-tv-z]{26}$", "description": "Unique ID for the policy.\n Pattern: pol_[0-9a-hjkmnp-tv-z]{26}" }, "name": { "type": "string", "examples": [ "GDPR Compliance Policy" ], "title": "name", "minLength": 1, "description": "Policy name." }, "created_at": { "title": "created_at", "description": "Timestamp when policy was created.", "$ref": "#/components/schemas/google.protobuf.Timestamp" } }, "title": "Policy", "required": [ "id", "name", "created_at" ], "additionalProperties": false, "description": "Policy represents a governance policy." }, "factify.api.v1beta.ProcessingStatus": { "enum": [ "processing", "ready", "failed" ] }, "factify.api.v1beta.ResendOrganizationInviteRequest": { "type": "object", "properties": { "organization_id": { "type": "string", "title": "organization_id", "pattern": "^org_[0-9a-hjkmnp-tv-z]{26}$", "description": "Organization the invitation belongs to.\n Pattern: org_[0-9a-hjkmnp-tv-z]{26}" }, "invite_id": { "type": "string", "title": "invite_id", "pattern": "^inv_[0-9a-hjkmnp-tv-z]{26}$", "description": "Invitation ID to resend.\n Pattern: inv_[0-9a-hjkmnp-tv-z]{26}" } }, "title": "ResendOrganizationInviteRequest", "required": [ "organization_id", "invite_id" ], "additionalProperties": false, "description": "ResendOrganizationInviteRequest identifies an invitation to resend." }, "factify.api.v1beta.ResendOrganizationInviteResponse": { "type": "object", "properties": { "invite": { "title": "invite", "description": "The invitation with updated send count and timestamp.", "$ref": "#/components/schemas/factify.api.v1beta.OrganizationInvite" } }, "title": "ResendOrganizationInviteResponse", "required": [ "invite" ], "additionalProperties": false, "description": "ResendOrganizationInviteResponse contains the updated invitation." }, "factify.api.v1beta.RevokeApiKeyRequest": { "type": "object", "properties": { "api_key_id": { "type": "string", "title": "api_key_id", "pattern": "^key_[0-9a-hjkmnp-tv-z]{26}$", "description": "API key ID to revoke.\n Pattern: key_[0-9a-hjkmnp-tv-z]{26}" }, "reason": { "type": "string", "title": "reason", "maxLength": 500, "minLength": 1, "description": "Optional reason for revocation (for audit purposes).\n The revoking user's identity and timestamp are captured in audit logs separately.", "nullable": true } }, "title": "RevokeApiKeyRequest", "required": [ "api_key_id" ], "additionalProperties": false, "description": "RevokeApiKeyRequest identifies an API key to revoke." }, "factify.api.v1beta.RevokeApiKeyResponse": { "type": "object", "properties": { "api_key": { "title": "api_key", "description": "The revoked API key with updated revoked_at timestamp.", "$ref": "#/components/schemas/factify.api.v1beta.ApiKey" } }, "title": "RevokeApiKeyResponse", "required": [ "api_key" ], "additionalProperties": false, "description": "RevokeApiKeyResponse contains the revoked API key." }, "factify.api.v1beta.RevokeOrganizationInviteRequest": { "type": "object", "properties": { "organization_id": { "type": "string", "title": "organization_id", "pattern": "^org_[0-9a-hjkmnp-tv-z]{26}$", "description": "Organization the invitation belongs to.\n Pattern: org_[0-9a-hjkmnp-tv-z]{26}" }, "invite_id": { "type": "string", "title": "invite_id", "pattern": "^inv_[0-9a-hjkmnp-tv-z]{26}$", "description": "Invitation ID to revoke.\n Pattern: inv_[0-9a-hjkmnp-tv-z]{26}" } }, "title": "RevokeOrganizationInviteRequest", "required": [ "organization_id", "invite_id" ], "additionalProperties": false, "description": "RevokeOrganizationInviteRequest identifies an invitation to revoke." }, "factify.api.v1beta.RevokeOrganizationInviteResponse": { "type": "object", "properties": { "invite": { "title": "invite", "description": "The revoked invitation with updated status.", "$ref": "#/components/schemas/factify.api.v1beta.OrganizationInvite" } }, "title": "RevokeOrganizationInviteResponse", "required": [ "invite" ], "additionalProperties": false, "description": "RevokeOrganizationInviteResponse contains the revoked invitation." }, "factify.api.v1beta.StringFilter": { "type": "object", "properties": { "contains": { "type": "string", "title": "contains", "description": "Case-insensitive substring match.\n REST: ?field.contains=value", "nullable": true }, "exact": { "type": "string", "title": "exact", "description": "Exact match (case-sensitive).\n REST: ?field.exact=value", "nullable": true } }, "title": "StringFilter", "additionalProperties": false, "description": "StringFilter provides flexible string matching options.\n Used with Vanguard dot notation: ?email.contains=john or ?email.exact=john@example.com\n\n Note on case sensitivity: `contains` is case-insensitive for user-friendly partial\n matching (e.g., finding \"john\" matches \"John.Doe@...\"). `exact` is case-sensitive\n for precise lookups where the caller knows the exact value. For email addresses,\n this matches RFC 5321 behavior where the domain is case-insensitive but local-part\n handling varies by provider.\nmutually_exclusive // only one of contains or exact can be specified\n" }, "factify.api.v1beta.TimestampFilter": { "type": "object", "properties": { "after": { "title": "after", "description": "Return results after this timestamp (inclusive).", "nullable": true, "$ref": "#/components/schemas/google.protobuf.Timestamp" }, "before": { "title": "before", "description": "Return results before this timestamp (inclusive).", "nullable": true, "$ref": "#/components/schemas/google.protobuf.Timestamp" } }, "title": "TimestampFilter", "additionalProperties": false, "description": "TimestampFilter specifies a time range for filtering.\n Used with Vanguard dot notation: ?created.after=...\u0026created.before=..." }, "factify.api.v1beta.UpdateDocumentRequest": { "type": "object", "properties": { "document_id": { "type": "string", "title": "document_id", "pattern": "^doc_[0-9a-hjkmnp-tv-z]{26}$", "description": "Document ID to update.\n Pattern: doc_[0-9a-hjkmnp-tv-z]{26}" }, "title": { "type": "string", "title": "title", "maxLength": 255, "minLength": 1, "description": "New title (1-255 characters).", "nullable": true }, "description": { "type": "string", "title": "description", "maxLength": 2000, "description": "New description (max 2000 characters).", "nullable": true } }, "title": "UpdateDocumentRequest", "additionalProperties": false, "description": "UpdateDocumentRequest specifies fields to update on a document." }, "factify.api.v1beta.UpdateDocumentResponse": { "type": "object", "properties": { "document": { "title": "document", "description": "The updated document.", "$ref": "#/components/schemas/factify.api.v1beta.Document" } }, "title": "UpdateDocumentResponse", "required": [ "document" ], "additionalProperties": false, "description": "UpdateDocumentResponse contains the updated document." }, "factify.api.v1beta.UpdateVersionRequest": { "type": "object", "properties": { "version_id": { "type": "string", "title": "version_id", "pattern": "^ver_[0-9a-hjkmnp-tv-z]{26}$", "description": "Version ID to update.\n Pattern: ver_[0-9a-hjkmnp-tv-z]{26}" }, "title": { "type": "string", "title": "title", "maxLength": 255, "description": "New title (max 255 characters).", "nullable": true }, "description": { "type": "string", "title": "description", "description": "New description.", "nullable": true } }, "title": "UpdateVersionRequest", "additionalProperties": false, "description": "UpdateVersionRequest specifies fields to update on a version." }, "factify.api.v1beta.User": { "type": "object", "properties": { "id": { "type": "string", "examples": [ "user_01h2xcejqtf2nbrexx3vqjhp41" ], "title": "id", "pattern": "^(user|svc)_[0-9a-hjkmnp-tv-z]{26}$", "description": "Unique ID for the user or service account.\n Pattern: user_[0-9a-hjkmnp-tv-z]{26} for users, svc_[0-9a-hjkmnp-tv-z]{26} for service accounts" }, "type": { "title": "type", "description": "User type discriminator.", "$ref": "#/components/schemas/factify.api.v1beta.UserType" }, "name": { "type": "string", "examples": [ "John Doe" ], "title": "name", "minLength": 1, "description": "Display name of the user or service account." } }, "title": "User", "required": [ "id", "type", "name" ], "additionalProperties": false, "description": "User represents a user or service account." }, "factify.api.v1beta.UserType": { "enum": [ "user_account", "service_account" ] }, "factify.api.v1beta.Version": { "type": "object", "properties": { "id": { "type": "string", "examples": [ "ver_01h2abcd1234efgh5678jkmnpt" ], "title": "id", "pattern": "^ver_[0-9a-hjkmnp-tv-z]{26}$", "description": "Unique ID for the version.\n Pattern: ver_[0-9a-hjkmnp-tv-z]{26}" }, "document_id": { "type": "string", "examples": [ "doc_01h2xcejqtf2nbrexx3vqjhp41" ], "title": "document_id", "pattern": "^doc_[0-9a-hjkmnp-tv-z]{26}$", "description": "Parent document ID.\n Pattern: doc_[0-9a-hjkmnp-tv-z]{26}" }, "url": { "type": "string", "examples": [ "https://d.factify.com/d/01h2xcejqtf2nbrexx3vqjhp41/v/1" ], "title": "url", "format": "uri", "description": "URL for accessing this version on Factify." }, "title": { "type": "string", "examples": [ "Version 2.0" ], "title": "title", "maxLength": 255, "description": "Optional version title.", "nullable": true }, "description": { "type": "string", "title": "description", "description": "Description of changes in this version.", "nullable": true }, "processing_status": { "title": "processing_status", "description": "Processing status.", "$ref": "#/components/schemas/factify.api.v1beta.ProcessingStatus" }, "created_at": { "title": "created_at", "description": "Timestamp when version was created.", "$ref": "#/components/schemas/google.protobuf.Timestamp" }, "created_by": { "title": "created_by", "description": "User who created the version.", "$ref": "#/components/schemas/factify.api.v1beta.User" } }, "title": "Version", "required": [ "id", "document_id", "url", "processing_status", "created_at", "created_by" ], "additionalProperties": false, "description": "Version represents a document version." }, "factify.api.v1beta.VersionRef": { "type": "object", "properties": { "id": { "type": "string", "examples": [ "ver_01h2abcd1234efgh5678jkmnpt" ], "title": "id", "pattern": "^ver_[0-9a-hjkmnp-tv-z]{26}$", "description": "Version ID.\n Pattern: ver_[0-9a-hjkmnp-tv-z]{26}" } }, "title": "VersionRef", "required": [ "id" ], "additionalProperties": false, "description": "VersionRef is a lightweight reference to a version." }, "google.protobuf.Empty": { "type": "object", "description": "A generic empty message that you can re-use to avoid defining duplicated\n empty messages in your APIs. A typical example is to use it as the request\n or the response type of an API method. For instance:\n\n service Foo {\n rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty);\n }" }, "google.protobuf.Timestamp": { "type": "string", "examples": [ "2023-01-15T01:30:15.01Z", "2024-12-25T12:00:00Z" ], "format": "date-time", "description": "A Timestamp represents a point in time independent of any time zone or local\n calendar, encoded as a count of seconds and fractions of seconds at\n nanosecond resolution. The count is relative to an epoch at UTC midnight on\n January 1, 1970, in the proleptic Gregorian calendar which extends the\n Gregorian calendar backwards to year one.\n\n All minutes are 60 seconds long. Leap seconds are \"smeared\" so that no leap\n second table is needed for interpretation, using a [24-hour linear\n smear](https://developers.google.com/time/smear).\n\n The range is from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59.999999999Z. By\n restricting to that range, we ensure that we can convert to and from [RFC\n 3339](https://www.ietf.org/rfc/rfc3339.txt) date strings.\n\n # Examples\n\n Example 1: Compute Timestamp from POSIX `time()`.\n\n Timestamp timestamp;\n timestamp.set_seconds(time(NULL));\n timestamp.set_nanos(0);\n\n Example 2: Compute Timestamp from POSIX `gettimeofday()`.\n\n struct timeval tv;\n gettimeofday(\u0026tv, NULL);\n\n Timestamp timestamp;\n timestamp.set_seconds(tv.tv_sec);\n timestamp.set_nanos(tv.tv_usec * 1000);\n\n Example 3: Compute Timestamp from Win32 `GetSystemTimeAsFileTime()`.\n\n FILETIME ft;\n GetSystemTimeAsFileTime(\u0026ft);\n UINT64 ticks = (((UINT64)ft.dwHighDateTime) \u003c\u003c 32) | ft.dwLowDateTime;\n\n // A Windows tick is 100 nanoseconds. Windows epoch 1601-01-01T00:00:00Z\n // is 11644473600 seconds before Unix epoch 1970-01-01T00:00:00Z.\n Timestamp timestamp;\n timestamp.set_seconds((INT64) ((ticks / 10000000) - 11644473600LL));\n timestamp.set_nanos((INT32) ((ticks % 10000000) * 100));\n\n Example 4: Compute Timestamp from Java `System.currentTimeMillis()`.\n\n long millis = System.currentTimeMillis();\n\n Timestamp timestamp = Timestamp.newBuilder().setSeconds(millis / 1000)\n .setNanos((int) ((millis % 1000) * 1000000)).build();\n\n Example 5: Compute Timestamp from Java `Instant.now()`.\n\n Instant now = Instant.now();\n\n Timestamp timestamp =\n Timestamp.newBuilder().setSeconds(now.getEpochSecond())\n .setNanos(now.getNano()).build();\n\n Example 6: Compute Timestamp from current time in Python.\n\n timestamp = Timestamp()\n timestamp.GetCurrentTime()\n\n # JSON Mapping\n\n In JSON format, the Timestamp type is encoded as a string in the\n [RFC 3339](https://www.ietf.org/rfc/rfc3339.txt) format. That is, the\n format is \"{year}-{month}-{day}T{hour}:{min}:{sec}[.{frac_sec}]Z\"\n where {year} is always expressed using four digits while {month}, {day},\n {hour}, {min}, and {sec} are zero-padded to two digits each. The fractional\n seconds, which can go up to 9 digits (i.e. up to 1 nanosecond resolution),\n are optional. The \"Z\" suffix indicates the timezone (\"UTC\"); the timezone\n is required. A proto3 JSON serializer should always use UTC (as indicated by\n \"Z\") when printing the Timestamp type and a proto3 JSON parser should be\n able to accept both UTC and other timezones (as indicated by an offset).\n\n For example, \"2017-01-15T01:30:15.01Z\" encodes 15.01 seconds past\n 01:30 UTC on January 15, 2017.\n\n In JavaScript, one can convert a Date object to this format using the\n standard\n [toISOString()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString)\n method. In Python, a standard `datetime.datetime` object can be converted\n to this format using\n [`strftime`](https://docs.python.org/2/library/time.html#time.strftime) with\n the time format spec '%Y-%m-%dT%H:%M:%S.%fZ'. Likewise, in Java, one can use\n the Joda Time's [`ISODateTimeFormat.dateTime()`](\n http://joda-time.sourceforge.net/apidocs/org/joda/time/format/ISODateTimeFormat.html#dateTime()\n ) to obtain a formatter capable of generating timestamps in this format." } } }, "paths": { "/v1beta/api-keys": { "get": { "tags": [ "API Keys", "IdentityService" ], "summary": "List API keys", "description": "Lists API keys for an organization.", "operationId": "listApiKeys", "parameters": [ { "name": "organization_id", "in": "query", "description": "Organization ID to list keys for.\n Pattern: org_[0-9a-hjkmnp-tv-z]{26}", "required": true, "schema": { "type": "string", "title": "organization_id", "pattern": "^org_[0-9a-hjkmnp-tv-z]{26}$", "description": "Organization ID to list keys for.\n Pattern: org_[0-9a-hjkmnp-tv-z]{26}" } }, { "name": "include_revoked", "in": "query", "description": "Whether to include revoked keys in the response.\n Default: false (only active and expired-but-not-revoked keys are returned).\n When true, all keys including revoked ones are returned.\n Note: Expired keys are always included unless also revoked; use is_active to filter.", "schema": { "type": "boolean", "title": "include_revoked", "description": "Whether to include revoked keys in the response.\n Default: false (only active and expired-but-not-revoked keys are returned).\n When true, all keys including revoked ones are returned.\n Note: Expired keys are always included unless also revoked; use is_active to filter." } }, { "name": "page_token", "in": "query", "description": "Opaque pagination token from a previous response.", "schema": { "type": "string", "title": "page_token", "description": "Opaque pagination token from a previous response." } }, { "name": "page_size", "in": "query", "description": "Maximum number of items to return per page (1-100). Default: 50.", "schema": { "type": "integer", "title": "page_size", "maximum": 100, "minimum": 1, "format": "int32", "description": "Maximum number of items to return per page (1-100). Default: 50." } } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/factify.api.v1beta.ListApiKeysResponse" } } } } } }, "post": { "tags": [ "API Keys", "IdentityService" ], "summary": "Create an API key", "description": "Creates a new API key for the specified organization. The secret is only returned once in the response and cannot be retrieved later.", "operationId": "createApiKey", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/factify.api.v1beta.CreateApiKeyRequest" } } }, "required": true }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/factify.api.v1beta.CreateApiKeyResponse" } } } } } } }, "/v1beta/api-keys/{api_key_id}/revoke": { "post": { "tags": [ "API Keys", "IdentityService" ], "summary": "Revoke an API key", "description": "Revokes an API key, immediately preventing it from being used for authentication.", "operationId": "revokeApiKey", "parameters": [ { "name": "api_key_id", "in": "path", "description": "API key ID to revoke.\n Pattern: key_[0-9a-hjkmnp-tv-z]{26}", "required": true, "schema": { "type": "string", "title": "api_key_id", "pattern": "^key_[0-9a-hjkmnp-tv-z]{26}$", "description": "API key ID to revoke.\n Pattern: key_[0-9a-hjkmnp-tv-z]{26}" } } ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "api_key_id": { "type": "string", "title": "api_key_id", "pattern": "^key_[0-9a-hjkmnp-tv-z]{26}$", "description": "API key ID to revoke.\n Pattern: key_[0-9a-hjkmnp-tv-z]{26}" }, "reason": { "type": "string", "title": "reason", "maxLength": 500, "minLength": 1, "description": "Optional reason for revocation (for audit purposes).\n The revoking user's identity and timestamp are captured in audit logs separately.", "nullable": true } }, "title": "RevokeApiKeyRequest", "required": [ "api_key_id" ], "additionalProperties": false, "description": "RevokeApiKeyRequest identifies an API key to revoke." } } }, "required": true }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/factify.api.v1beta.RevokeApiKeyResponse" } } } } } } }, "/v1beta/documents": { "get": { "tags": [ "DocumentService" ], "summary": "List documents", "description": "List documents with cursor-based pagination.", "operationId": "listDocuments", "parameters": [ { "name": "page_token", "in": "query", "description": "Opaque pagination token from a previous response.\n Pass next_page_token or prev_page_token from a previous response to continue pagination.\n Empty or omitted for the first page.", "schema": { "type": "string", "examples": [ "eyJpZCI6ImRvY18wMWgyeGNlanF0ZjJuYnJleHgzdnFqaHA0MSIsImQiOiJuZXh0In0" ], "title": "page_token", "description": "Opaque pagination token from a previous response.\n Pass next_page_token or prev_page_token from a previous response to continue pagination.\n Empty or omitted for the first page." } }, { "name": "page_size", "in": "query", "description": "Maximum number of items to return per page (1-100). Default: 50.", "schema": { "type": "integer", "title": "page_size", "maximum": 100, "minimum": 1, "format": "int32", "description": "Maximum number of items to return per page (1-100). Default: 50." } }, { "name": "created.after.seconds", "in": "query", "description": "Represents seconds of UTC time since Unix epoch\n 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to\n 9999-12-31T23:59:59Z inclusive.", "schema": { "type": [ "integer", "string" ], "title": "seconds", "format": "int64", "description": "Represents seconds of UTC time since Unix epoch\n 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to\n 9999-12-31T23:59:59Z inclusive." } }, { "name": "created.after.nanos", "in": "query", "description": "Non-negative fractions of a second at nanosecond resolution. Negative\n second values with fractions must still have non-negative nanos values\n that count forward in time. Must be from 0 to 999,999,999\n inclusive.", "schema": { "type": "integer", "title": "nanos", "format": "int32", "description": "Non-negative fractions of a second at nanosecond resolution. Negative\n second values with fractions must still have non-negative nanos values\n that count forward in time. Must be from 0 to 999,999,999\n inclusive." } }, { "name": "created_by_id", "in": "query", "description": "Filter by creator ID(s) (user or service account). Returns documents matching ANY of the specified IDs.\n Accepts single value or comma-separated list.\n REST: ?created_by_id=user_01h2xcejqtf2nbrexx3vqjhp41 or ?created_by_id=user_xxx,svc_yyy", "schema": { "type": "string", "title": "created_by_id", "description": "Filter by creator ID(s) (user or service account). Returns documents matching ANY of the specified IDs.\n Accepts single value or comma-separated list.\n REST: ?created_by_id=user_01h2xcejqtf2nbrexx3vqjhp41 or ?created_by_id=user_xxx,svc_yyy" } }, { "name": "access_level", "in": "query", "description": "Filter by access level(s). Returns documents matching ANY of the specified levels.\n Accepts single value or comma-separated list.\n REST: ?access_level=private or ?access_level=private,organization\n Accepts clean enum names (case-insensitive): \"private\", \"organization\", \"public\"", "schema": { "type": "string", "title": "access_level", "description": "Filter by access level(s). Returns documents matching ANY of the specified levels.\n Accepts single value or comma-separated list.\n REST: ?access_level=private or ?access_level=private,organization\n Accepts clean enum names (case-insensitive): \"private\", \"organization\", \"public\"" } }, { "name": "processing_status", "in": "query", "description": "Filter by processing status(es). Returns documents matching ANY of the specified statuses.\n Accepts single value or comma-separated list.\n REST: ?processing_status=ready or ?processing_status=processing,ready,failed\n Accepts clean enum names (case-insensitive): \"processing\", \"ready\", \"failed\"", "schema": { "type": "string", "title": "processing_status", "description": "Filter by processing status(es). Returns documents matching ANY of the specified statuses.\n Accepts single value or comma-separated list.\n REST: ?processing_status=ready or ?processing_status=processing,ready,failed\n Accepts clean enum names (case-insensitive): \"processing\", \"ready\", \"failed\"" } } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/factify.api.v1beta.ListDocumentsResponse" } } } } } }, "post": { "tags": [ "DocumentService" ], "summary": "Create a document", "description": "Creates a new document by uploading a PDF file.", "operationId": "createDocument", "requestBody": { "content": { "multipart/form-data": { "schema": { "$ref": "#/components/schemas/factify.api.v1beta.CreateDocumentRequest" } } }, "required": true }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/factify.api.v1beta.CreateDocumentResponse" } } } } } } }, "/v1beta/documents/{document_id}": { "get": { "tags": [ "DocumentService" ], "summary": "Retrieve a document", "description": "Retrieve a document by ID.", "operationId": "getDocument", "parameters": [ { "name": "document_id", "in": "path", "description": "Document ID.\n Pattern: doc_[0-9a-hjkmnp-tv-z]{26}", "required": true, "schema": { "type": "string", "title": "document_id", "pattern": "^doc_[0-9a-hjkmnp-tv-z]{26}$", "description": "Document ID.\n Pattern: doc_[0-9a-hjkmnp-tv-z]{26}" } } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/factify.api.v1beta.GetDocumentResponse" } } } } } }, "patch": { "tags": [ "DocumentService" ], "summary": "Update a document", "description": "Update document title and description.", "operationId": "updateDocument", "parameters": [ { "name": "document_id", "in": "path", "description": "Document ID to update.\n Pattern: doc_[0-9a-hjkmnp-tv-z]{26}", "required": true, "schema": { "type": "string", "title": "document_id", "pattern": "^doc_[0-9a-hjkmnp-tv-z]{26}$", "description": "Document ID to update.\n Pattern: doc_[0-9a-hjkmnp-tv-z]{26}" } } ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "document_id": { "type": "string", "title": "document_id", "pattern": "^doc_[0-9a-hjkmnp-tv-z]{26}$", "description": "Document ID to update.\n Pattern: doc_[0-9a-hjkmnp-tv-z]{26}" }, "title": { "type": "string", "title": "title", "maxLength": 255, "minLength": 1, "description": "New title (1-255 characters).", "nullable": true }, "description": { "type": "string", "title": "description", "maxLength": 2000, "description": "New description (max 2000 characters).", "nullable": true } }, "title": "UpdateDocumentRequest", "additionalProperties": false, "description": "UpdateDocumentRequest specifies fields to update on a document." } } }, "required": true }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/factify.api.v1beta.UpdateDocumentResponse" } } } } } } }, "/v1beta/documents/{document_id}/entry_page": { "post": { "tags": [ "Sharing \u0026 Distribution", "EntryPageService" ], "summary": "Generate entry page", "description": "Generate and return a temporary downloadable URL for the document entry page PDF.", "operationId": "generateDocumentEntryPage", "parameters": [ { "name": "document_id", "in": "path", "description": "Document ID.\n Pattern: doc_[0-9a-hjkmnp-tv-z]{26}", "required": true, "schema": { "type": "string", "title": "document_id", "pattern": "^doc_[0-9a-hjkmnp-tv-z]{26}$", "description": "Document ID.\n Pattern: doc_[0-9a-hjkmnp-tv-z]{26}" } } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/factify.api.v1beta.GenerateEntryPageResponse" } } } } } } }, "/v1beta/documents/{document_id}/policies": { "get": { "tags": [ "PolicyService" ], "summary": "List document policies", "description": "List policies attached to a document.", "operationId": "listDocumentPolicies", "parameters": [ { "name": "document_id", "in": "path", "description": "Document ID.\n Pattern: doc_[0-9a-hjkmnp-tv-z]{26}", "required": true, "schema": { "type": "string", "title": "document_id", "pattern": "^doc_[0-9a-hjkmnp-tv-z]{26}$", "description": "Document ID.\n Pattern: doc_[0-9a-hjkmnp-tv-z]{26}" } }, { "name": "page_token", "in": "query", "description": "Opaque pagination token from a previous response.\n Pass next_page_token or prev_page_token from a previous response to continue pagination.\n Empty or omitted for the first page.", "schema": { "type": "string", "examples": [ "eyJpZCI6InBvbF8wMWgyeGNlanF0ZjJuYnJleHgzdnFqaHA0MSIsImQiOiJuZXh0IiwiZiI6eyJkb2NfaWQiOiJkb2NfMDFoMnhjZWpxdGYybmJyZXh4M3ZxamhwNDEifX0" ], "title": "page_token", "description": "Opaque pagination token from a previous response.\n Pass next_page_token or prev_page_token from a previous response to continue pagination.\n Empty or omitted for the first page." } }, { "name": "page_size", "in": "query", "description": "Maximum number of items to return per page (1-100). Default: 50.", "schema": { "type": "integer", "title": "page_size", "maximum": 100, "minimum": 1, "format": "int32", "description": "Maximum number of items to return per page (1-100). Default: 50." } } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/factify.api.v1beta.ListDocumentPoliciesResponse" } } } } } }, "post": { "tags": [ "PolicyService" ], "summary": "Attach a policy", "description": "Attach a policy to a document.", "operationId": "attachDocumentPolicy", "parameters": [ { "name": "document_id", "in": "path", "description": "Document ID.\n Pattern: doc_[0-9a-hjkmnp-tv-z]{26}", "required": true, "schema": { "type": "string", "title": "document_id", "pattern": "^doc_[0-9a-hjkmnp-tv-z]{26}$", "description": "Document ID.\n Pattern: doc_[0-9a-hjkmnp-tv-z]{26}" } } ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "document_id": { "type": "string", "title": "document_id", "pattern": "^doc_[0-9a-hjkmnp-tv-z]{26}$", "description": "Document ID.\n Pattern: doc_[0-9a-hjkmnp-tv-z]{26}" }, "policy_id": { "type": "string", "title": "policy_id", "pattern": "^pol_[0-9a-hjkmnp-tv-z]{26}$", "description": "Policy ID to attach.\n Pattern: pol_[0-9a-hjkmnp-tv-z]{26}" } }, "title": "AttachPolicyRequest", "required": [ "policy_id" ], "additionalProperties": false, "description": "AttachPolicyRequest specifies a policy to attach to a document." } } }, "required": true }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/factify.api.v1beta.DocumentPolicy" } } } } } } }, "/v1beta/documents/{document_id}/policies/{policy_id}": { "delete": { "tags": [ "PolicyService" ], "summary": "Detach a policy", "description": "Detach a policy from a document.", "operationId": "detachDocumentPolicy", "parameters": [ { "name": "document_id", "in": "path", "description": "Document ID.\n Pattern: doc_[0-9a-hjkmnp-tv-z]{26}", "required": true, "schema": { "type": "string", "title": "document_id", "pattern": "^doc_[0-9a-hjkmnp-tv-z]{26}$", "description": "Document ID.\n Pattern: doc_[0-9a-hjkmnp-tv-z]{26}" } }, { "name": "policy_id", "in": "path", "description": "Policy ID to detach.\n Pattern: pol_[0-9a-hjkmnp-tv-z]{26}", "required": true, "schema": { "type": "string", "title": "policy_id", "pattern": "^pol_[0-9a-hjkmnp-tv-z]{26}$", "description": "Policy ID to detach.\n Pattern: pol_[0-9a-hjkmnp-tv-z]{26}" } } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/google.protobuf.Empty" } } } } } } }, "/v1beta/documents/{document_id}/versions": { "get": { "tags": [ "VersionService" ], "summary": "List document versions", "description": "List versions for a document with cursor-based pagination.", "operationId": "listDocumentVersions", "parameters": [ { "name": "document_id", "in": "path", "description": "Document ID to list versions for.\n Pattern: doc_[0-9a-hjkmnp-tv-z]{26}", "required": true, "schema": { "type": "string", "title": "document_id", "pattern": "^doc_[0-9a-hjkmnp-tv-z]{26}$", "description": "Document ID to list versions for.\n Pattern: doc_[0-9a-hjkmnp-tv-z]{26}" } }, { "name": "page_token", "in": "query", "description": "Opaque pagination token from a previous response.\n Pass next_page_token or prev_page_token from a previous response to continue pagination.\n Empty or omitted for the first page.", "schema": { "type": "string", "examples": [ "eyJpZCI6InZlcl8wMWgyYWJjZDEyMzRlZmdoNTY3OGprbW5wdCIsImQiOiJuZXh0IiwiZiI6eyJkb2NfaWQiOiJkb2NfMDFoMnhjZWpxdGYybmJyZXh4M3ZxamhwNDEifX0" ], "title": "page_token", "description": "Opaque pagination token from a previous response.\n Pass next_page_token or prev_page_token from a previous response to continue pagination.\n Empty or omitted for the first page." } }, { "name": "page_size", "in": "query", "description": "Maximum number of items to return per page (1-100). Default: 50.", "schema": { "type": "integer", "title": "page_size", "maximum": 100, "minimum": 1, "format": "int32", "description": "Maximum number of items to return per page (1-100). Default: 50." } } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/factify.api.v1beta.ListVersionsResponse" } } } } } }, "post": { "tags": [ "VersionService" ], "summary": "Create a new version", "description": "Creates a new version by uploading a PDF file.", "operationId": "createDocumentVersion", "parameters": [ { "name": "document_id", "in": "path", "description": "Document ID to create version for.\n Pattern: doc_[0-9a-hjkmnp-tv-z]{26}", "required": true, "schema": { "type": "string", "title": "document_id", "pattern": "^doc_[0-9a-hjkmnp-tv-z]{26}$", "description": "Document ID to create version for.\n Pattern: doc_[0-9a-hjkmnp-tv-z]{26}" } } ], "requestBody": { "content": { "multipart/form-data": { "schema": { "$ref": "#/components/schemas/factify.api.v1beta.CreateVersionRequest" } } }, "required": true }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/factify.api.v1beta.Version" } } } } } } }, "/v1beta/organizations": { "get": { "tags": [ "Organizations", "OrganizationService" ], "summary": "List organizations", "description": "List organizations the caller has access to.", "operationId": "listOrganizations", "parameters": [ { "name": "page_token", "in": "query", "description": "Opaque pagination token from a previous response.", "schema": { "type": "string", "title": "page_token", "description": "Opaque pagination token from a previous response." } }, { "name": "page_size", "in": "query", "description": "Maximum number of items to return per page (1-100). Default: 50.", "schema": { "type": "integer", "title": "page_size", "maximum": 100, "minimum": 1, "format": "int32", "description": "Maximum number of items to return per page (1-100). Default: 50." } } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/factify.api.v1beta.ListOrganizationsResponse" } } } } } }, "post": { "tags": [ "Organizations", "OrganizationService" ], "summary": "Create an organization", "description": "Creates a new organization. The authenticated user becomes the organization owner.", "operationId": "createOrganization", "requestBody": { "content": { "application/json": { "schema": { "$ref": "#/components/schemas/factify.api.v1beta.CreateOrganizationRequest" } } }, "required": true }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/factify.api.v1beta.CreateOrganizationResponse" } } } } } } }, "/v1beta/organizations/{organization_id}": { "get": { "tags": [ "Organizations", "OrganizationService" ], "summary": "Retrieve an organization", "description": "Retrieve an organization by ID.", "operationId": "getOrganization", "parameters": [ { "name": "organization_id", "in": "path", "description": "Organization ID.\n Pattern: org_[0-9a-hjkmnp-tv-z]{26}", "required": true, "schema": { "type": "string", "title": "organization_id", "pattern": "^org_[0-9a-hjkmnp-tv-z]{26}$", "description": "Organization ID.\n Pattern: org_[0-9a-hjkmnp-tv-z]{26}" } } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/factify.api.v1beta.GetOrganizationResponse" } } } } } } }, "/v1beta/organizations/{organization_id}/invites": { "get": { "tags": [ "Organizations", "OrganizationService" ], "summary": "List organization invitations", "description": "List invitations for an organization. Requires permission to invite organization members.", "operationId": "listOrganizationInvites", "parameters": [ { "name": "organization_id", "in": "path", "description": "Organization to list invitations for.\n Pattern: org_[0-9a-hjkmnp-tv-z]{26}", "required": true, "schema": { "type": "string", "title": "organization_id", "pattern": "^org_[0-9a-hjkmnp-tv-z]{26}$", "description": "Organization to list invitations for.\n Pattern: org_[0-9a-hjkmnp-tv-z]{26}" } }, { "name": "status", "in": "query", "description": "Filter by status. If empty, returns all invitations.\n ORGANIZATION_INVITE_STATUS_UNSPECIFIED (0) is not a valid filter value.", "schema": { "type": "array", "items": { "$ref": "#/components/schemas/factify.api.v1beta.OrganizationInviteStatus" }, "title": "status", "description": "Filter by status. If empty, returns all invitations.\n ORGANIZATION_INVITE_STATUS_UNSPECIFIED (0) is not a valid filter value." } }, { "name": "page_token", "in": "query", "description": "Opaque pagination token from a previous response.", "schema": { "type": "string", "title": "page_token", "description": "Opaque pagination token from a previous response." } }, { "name": "page_size", "in": "query", "description": "Maximum number of items to return per page (1-100). Default: 50.", "schema": { "type": "integer", "title": "page_size", "maximum": 100, "minimum": 1, "format": "int32", "description": "Maximum number of items to return per page (1-100). Default: 50." } }, { "name": "email.contains", "in": "query", "description": "Case-insensitive substring match.\n REST: ?field.contains=value", "schema": { "type": "string", "title": "contains", "description": "Case-insensitive substring match.\n REST: ?field.contains=value" } }, { "name": "email.exact", "in": "query", "description": "Exact match (case-sensitive).\n REST: ?field.exact=value", "schema": { "type": "string", "title": "exact", "description": "Exact match (case-sensitive).\n REST: ?field.exact=value" } }, { "name": "sender_id", "in": "query", "description": "Filter by sender. Only returns invitations sent by this user.\n REST: ?sender_id=user_01h2xcejqtf2nbrexx3vqjhp41\n Pattern: user_[0-9a-hjkmnp-tv-z]{26}", "schema": { "type": "string", "title": "sender_id", "pattern": "^user_[0-9a-hjkmnp-tv-z]{26}$", "description": "Filter by sender. Only returns invitations sent by this user.\n REST: ?sender_id=user_01h2xcejqtf2nbrexx3vqjhp41\n Pattern: user_[0-9a-hjkmnp-tv-z]{26}" } }, { "name": "created.after.seconds", "in": "query", "description": "Represents seconds of UTC time since Unix epoch\n 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to\n 9999-12-31T23:59:59Z inclusive.", "schema": { "type": [ "integer", "string" ], "title": "seconds", "format": "int64", "description": "Represents seconds of UTC time since Unix epoch\n 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to\n 9999-12-31T23:59:59Z inclusive." } }, { "name": "created.after.nanos", "in": "query", "description": "Non-negative fractions of a second at nanosecond resolution. Negative\n second values with fractions must still have non-negative nanos values\n that count forward in time. Must be from 0 to 999,999,999\n inclusive.", "schema": { "type": "integer", "title": "nanos", "format": "int32", "description": "Non-negative fractions of a second at nanosecond resolution. Negative\n second values with fractions must still have non-negative nanos values\n that count forward in time. Must be from 0 to 999,999,999\n inclusive." } } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/factify.api.v1beta.ListOrganizationInvitesResponse" } } } } } }, "post": { "tags": [ "Organizations", "OrganizationService" ], "summary": "Invite a user to join an organization", "description": "Creates an invitation and sends an email to the specified address. Returns FAILED_PRECONDITION if the email belongs to an existing organization member. Idempotency: If a PENDING invitation already exists for this email, the existing invitation is resent with a new token and refreshed expiration. Expired or revoked invitations are ignored - a new invitation is created.", "operationId": "createOrganizationInvite", "parameters": [ { "name": "organization_id", "in": "path", "description": "Organization to invite the user to.\n Pattern: org_[0-9a-hjkmnp-tv-z]{26}", "required": true, "schema": { "type": "string", "title": "organization_id", "pattern": "^org_[0-9a-hjkmnp-tv-z]{26}$", "description": "Organization to invite the user to.\n Pattern: org_[0-9a-hjkmnp-tv-z]{26}" } } ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "organization_id": { "type": "string", "title": "organization_id", "pattern": "^org_[0-9a-hjkmnp-tv-z]{26}$", "description": "Organization to invite the user to.\n Pattern: org_[0-9a-hjkmnp-tv-z]{26}" }, "email": { "type": "string", "title": "email", "format": "email", "description": "Email address of the recipient." }, "message": { "type": "string", "title": "message", "maxLength": 2000, "description": "Optional custom message from the sender (max 2000 bytes to support ~500 multibyte characters).", "nullable": true }, "idempotency_key": { "type": "string", "title": "idempotency_key", "maxLength": 255, "description": "Reserved for future use. Currently, idempotency is based on email matching:\n if a PENDING invitation exists for the same email in this organization,\n that invitation is resent rather than creating a duplicate.", "nullable": true } }, "title": "CreateOrganizationInviteRequest", "required": [ "organization_id", "email" ], "additionalProperties": false, "description": "CreateOrganizationInviteRequest contains the data needed to create an invitation." } } }, "required": true }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/factify.api.v1beta.CreateOrganizationInviteResponse" } } } } } } }, "/v1beta/organizations/{organization_id}/invites/accept": { "post": { "tags": [ "Organizations", "OrganizationService" ], "summary": "Accept an invitation", "description": "Accepts the invitation and adds the authenticated user as a member of the organization. The authenticated user's verified email must match the invitation email (case-insensitive). Returns PERMISSION_DENIED if emails don't match, FAILED_PRECONDITION if the user is already a member, or NOT_FOUND if the invitation is invalid/expired.", "operationId": "acceptOrganizationInvite", "parameters": [ { "name": "organization_id", "in": "path", "description": "Organization the invitation belongs to (for validation).\n Pattern: org_[0-9a-hjkmnp-tv-z]{26}", "required": true, "schema": { "type": "string", "title": "organization_id", "pattern": "^org_[0-9a-hjkmnp-tv-z]{26}$", "description": "Organization the invitation belongs to (for validation).\n Pattern: org_[0-9a-hjkmnp-tv-z]{26}" } } ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "organization_id": { "type": "string", "title": "organization_id", "pattern": "^org_[0-9a-hjkmnp-tv-z]{26}$", "description": "Organization the invitation belongs to (for validation).\n Pattern: org_[0-9a-hjkmnp-tv-z]{26}" }, "token": { "type": "string", "title": "token", "maxLength": 128, "description": "The invitation token from the email link." } }, "title": "AcceptOrganizationInviteRequest", "required": [ "organization_id", "token" ], "additionalProperties": false, "description": "AcceptOrganizationInviteRequest contains the token to accept an invitation." } } }, "required": true }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/factify.api.v1beta.AcceptOrganizationInviteResponse" } } } } } } }, "/v1beta/organizations/{organization_id}/invites/{invite_id}/resend": { "post": { "tags": [ "Organizations", "OrganizationService" ], "summary": "Resend an invitation email", "description": "Resend an invitation email to the recipient. Useful if the original email was lost or expired. Requires permission to invite organization members.", "operationId": "resendOrganizationInvite", "parameters": [ { "name": "organization_id", "in": "path", "description": "Organization the invitation belongs to.\n Pattern: org_[0-9a-hjkmnp-tv-z]{26}", "required": true, "schema": { "type": "string", "title": "organization_id", "pattern": "^org_[0-9a-hjkmnp-tv-z]{26}$", "description": "Organization the invitation belongs to.\n Pattern: org_[0-9a-hjkmnp-tv-z]{26}" } }, { "name": "invite_id", "in": "path", "description": "Invitation ID to resend.\n Pattern: inv_[0-9a-hjkmnp-tv-z]{26}", "required": true, "schema": { "type": "string", "title": "invite_id", "pattern": "^inv_[0-9a-hjkmnp-tv-z]{26}$", "description": "Invitation ID to resend.\n Pattern: inv_[0-9a-hjkmnp-tv-z]{26}" } } ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "organization_id": { "type": "string", "title": "organization_id", "pattern": "^org_[0-9a-hjkmnp-tv-z]{26}$", "description": "Organization the invitation belongs to.\n Pattern: org_[0-9a-hjkmnp-tv-z]{26}" }, "invite_id": { "type": "string", "title": "invite_id", "pattern": "^inv_[0-9a-hjkmnp-tv-z]{26}$", "description": "Invitation ID to resend.\n Pattern: inv_[0-9a-hjkmnp-tv-z]{26}" } }, "title": "ResendOrganizationInviteRequest", "required": [ "organization_id", "invite_id" ], "additionalProperties": false, "description": "ResendOrganizationInviteRequest identifies an invitation to resend." } } }, "required": true }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/factify.api.v1beta.ResendOrganizationInviteResponse" } } } } } } }, "/v1beta/organizations/{organization_id}/invites/{invite_id}/revoke": { "post": { "tags": [ "Organizations", "OrganizationService" ], "summary": "Revoke an invitation", "description": "Revoke a pending invitation, preventing the recipient from joining. Requires permission to manage organization members.", "operationId": "revokeOrganizationInvite", "parameters": [ { "name": "organization_id", "in": "path", "description": "Organization the invitation belongs to.\n Pattern: org_[0-9a-hjkmnp-tv-z]{26}", "required": true, "schema": { "type": "string", "title": "organization_id", "pattern": "^org_[0-9a-hjkmnp-tv-z]{26}$", "description": "Organization the invitation belongs to.\n Pattern: org_[0-9a-hjkmnp-tv-z]{26}" } }, { "name": "invite_id", "in": "path", "description": "Invitation ID to revoke.\n Pattern: inv_[0-9a-hjkmnp-tv-z]{26}", "required": true, "schema": { "type": "string", "title": "invite_id", "pattern": "^inv_[0-9a-hjkmnp-tv-z]{26}$", "description": "Invitation ID to revoke.\n Pattern: inv_[0-9a-hjkmnp-tv-z]{26}" } } ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "organization_id": { "type": "string", "title": "organization_id", "pattern": "^org_[0-9a-hjkmnp-tv-z]{26}$", "description": "Organization the invitation belongs to.\n Pattern: org_[0-9a-hjkmnp-tv-z]{26}" }, "invite_id": { "type": "string", "title": "invite_id", "pattern": "^inv_[0-9a-hjkmnp-tv-z]{26}$", "description": "Invitation ID to revoke.\n Pattern: inv_[0-9a-hjkmnp-tv-z]{26}" } }, "title": "RevokeOrganizationInviteRequest", "required": [ "organization_id", "invite_id" ], "additionalProperties": false, "description": "RevokeOrganizationInviteRequest identifies an invitation to revoke." } } }, "required": true }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/factify.api.v1beta.RevokeOrganizationInviteResponse" } } } } } } }, "/v1beta/versions/{version_id}": { "get": { "tags": [ "VersionService" ], "summary": "Retrieve a version", "description": "Retrieve a specific version by ID.", "operationId": "getVersion", "parameters": [ { "name": "version_id", "in": "path", "description": "Version ID to retrieve.\n Pattern: ver_[0-9a-hjkmnp-tv-z]{26}", "required": true, "schema": { "type": "string", "title": "version_id", "pattern": "^ver_[0-9a-hjkmnp-tv-z]{26}$", "description": "Version ID to retrieve.\n Pattern: ver_[0-9a-hjkmnp-tv-z]{26}" } } ], "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/factify.api.v1beta.Version" } } } } } }, "patch": { "tags": [ "VersionService" ], "summary": "Update a version", "description": "Update version title and description.", "operationId": "updateVersion", "parameters": [ { "name": "version_id", "in": "path", "description": "Version ID to update.\n Pattern: ver_[0-9a-hjkmnp-tv-z]{26}", "required": true, "schema": { "type": "string", "title": "version_id", "pattern": "^ver_[0-9a-hjkmnp-tv-z]{26}$", "description": "Version ID to update.\n Pattern: ver_[0-9a-hjkmnp-tv-z]{26}" } } ], "requestBody": { "content": { "application/json": { "schema": { "type": "object", "properties": { "version_id": { "type": "string", "title": "version_id", "pattern": "^ver_[0-9a-hjkmnp-tv-z]{26}$", "description": "Version ID to update.\n Pattern: ver_[0-9a-hjkmnp-tv-z]{26}" }, "title": { "type": "string", "title": "title", "maxLength": 255, "description": "New title (max 255 characters).", "nullable": true }, "description": { "type": "string", "title": "description", "description": "New description.", "nullable": true } }, "title": "UpdateVersionRequest", "additionalProperties": false, "description": "UpdateVersionRequest specifies fields to update on a version." } } }, "required": true }, "responses": { "200": { "description": "Success", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/factify.api.v1beta.Version" } } } } } } } } }