{
	"$id": "https://integrate.automatedoperations.com/schemas/submission.schema.json",
	"$schema": "https://json-schema.org/draft/2020-12/schema",
	"additionalProperties": false,
	"description": "Machine-readable spec for a partner-submitted integration manifest. Authored by the partner (or their agent) via integrate.automatedoperations.com; validated server-side before review. Subset of AO's provider manifest covering identity, capabilities, webhook contract, event mappings, and sync surfaces.",
	"properties": {
		"capabilities": {
			"additionalProperties": false,
			"properties": {
				"install_ready": {
					"description": "True when the provider supports end-to-end customer install. install_ready=false providers are documented in the catalog but BeginIntegrationInstall returns an error.",
					"type": "boolean"
				},
				"requires_webhook_secret": {
					"type": "boolean"
				},
				"supports_error_classification": {
					"description": "True when the provider implements ErrorClassifier (buckets provider errors into stable wire codes).",
					"type": "boolean"
				},
				"supports_hooks": {
					"description": "True when the provider implements HookManager (programmatically installs provider-side webhooks per resource).",
					"type": "boolean"
				},
				"supports_reactivate": {
					"description": "True when the provider implements Reactivatable (refresh creds on revoke+reinstall conflict).",
					"type": "boolean"
				},
				"supports_setup_picker": {
					"description": "True when the customer flow has a post-OAuth resource-selection page (/integrations/<name>/setup).",
					"type": "boolean"
				},
				"supports_subgroups": {
					"description": "Provider-specific: GitLab has subgroups, others don't. Drives whether the install loop writes routing targets for descendant resources.",
					"type": "boolean"
				}
			},
			"required": ["install_ready"],
			"type": "object"
		},
		"events": {
			"description": "Canonical → provider event mapping. The conformance suite asserts one fixture per entry.",
			"items": {
				"additionalProperties": false,
				"properties": {
					"action_field": {
						"description": "Dot-path into the payload that disambiguates the canonical (e.g. 'object_attributes.action' for GitLab MR).",
						"type": "string"
					},
					"canonical": {
						"description": "Canonical event_type matching the eventstore namespace regex.",
						"pattern": "^[a-z][a-z0-9_-]*[:.][a-z][a-z0-9_-]+$",
						"type": "string"
					},
					"id_strategy": {
						"enum": [
							"delivery_uuid",
							"checkout_sha",
							"object_attributes_id",
							"alert_correlation_id",
							"composite",
							"provider",
							"synthesize"
						],
						"type": "string"
					},
					"provider_event": {
						"description": "Provider-side event name (header value or object_kind).",
						"type": "string"
					}
				},
				"required": ["canonical", "provider_event"],
				"type": "object"
			},
			"type": "array"
		},
		"identity": {
			"additionalProperties": false,
			"properties": {
				"auth_pattern": {
					"enum": ["oauth", "github_app", "iam_trust", "credential_paste", "ingest"],
					"type": "string"
				},
				"category": {
					"enum": [
						"Source",
						"Cloud",
						"Observability",
						"Incident",
						"Comms",
						"Edge",
						"Data",
						"Finance",
						"IaC",
						"Custom"
					],
					"type": "string"
				},
				"description": {
					"type": "string"
				},
				"display_name": {
					"minLength": 1,
					"type": "string"
				},
				"docs_url": {
					"format": "uri",
					"type": "string"
				},
				"logo_slug": {
					"description": "Iconify logos collection slug (rendered via iconifyLogo() in catalog.go).",
					"type": "string"
				},
				"name": {
					"description": "Stable provider key — appears in URLs (/wh/{name}/{install_id}), DB rows, GraphQL. Matches the provider's Name() return.",
					"pattern": "^[a-z][a-z0-9_]+$",
					"type": "string"
				}
			},
			"required": ["name", "display_name", "category", "auth_pattern"],
			"type": "object"
		},
		"schema_version": {
			"const": 1,
			"description": "Manifest schema version. Bump when adding fields that change validator behavior."
		},
		"sync": {
			"additionalProperties": false,
			"properties": {
				"poll_interval": {
					"description": "Default metadata-sync cadence as a Go duration (e.g. '1h'). Contract §3: every provider with a poll surface declares one that respects upstream rate limits.",
					"pattern": "^[0-9]+(ns|us|ms|s|m|h)$",
					"type": "string"
				},
				"resources": {
					"items": {
						"additionalProperties": false,
						"properties": {
							"attributes": {
								"type": "object"
							},
							"endpoint": {
								"type": "string"
							},
							"key": {
								"type": "object"
							},
							"kind": {
								"description": "Graphstore kind (e.g. Repo) — must exist in graphstore/registry.go.",
								"type": "string"
							},
							"pagination": {
								"enum": ["rfc5988_link_next", "page_param", "page", "cursor", "none"],
								"type": "string"
							}
						},
						"required": ["kind", "endpoint"],
						"type": "object"
					},
					"type": "array"
				}
			},
			"type": "object"
		},
		"webhook": {
			"additionalProperties": false,
			"properties": {
				"delivery_id_header": {
					"type": "string"
				},
				"event_type_header": {
					"type": "string"
				},
				"header_normalize_suffix": {
					"description": "If set, this suffix is stripped from event_type_header values (e.g. ' Hook' for GitLab's 'Push Hook').",
					"type": "string"
				},
				"signature_header": {
					"type": "string"
				},
				"signature_scheme": {
					"enum": [
						"hmac_sha256",
						"hmac_sha512",
						"hmac_sha1",
						"bearer_token",
						"bearer_token_compare",
						"stripe_signature",
						"shared_secret",
						"none"
					],
					"type": "string"
				},
				"verification_secret_source": {
					"description": "Where the ingest router reads the HMAC key from. Empty/'webhook_secret_column' = the dedicated platform-config column (default). 'config:<field>' = a platform-config field (Vercel reuses the OAuth client secret). 'install:<field>' = per-install credentials map, resolved via WebhookResolver BEFORE verification (Cloudflare; prevents cross-tenant forgery).",
					"pattern": "^(webhook_secret_column|config:[a-z][a-z0-9_]*|install:[a-z][a-z0-9_]*)$",
					"type": "string"
				}
			},
			"type": "object"
		}
	},
	"required": ["schema_version", "identity", "capabilities"],
	"title": "AO Integration Submission",
	"type": "object"
}
