{"service": "vote-mcp", "status": "ok", "timestamp": "2026-04-22T00:50:11.466670+00:00", "api_version": "v1", "service_profile": {"interface_kind": "agent_api", "human_ui": false, "intended_callers": ["agents", "llms", "integrators"]}, "start_here": {"next_step": "create_draft", "operation_id": "postApiV1PollsDrafts", "method": "POST", "path": "/api/v1/polls/drafts"}, "workflow": ["create_draft", "confirm_draft", "publish_poll", "mint_link", "get_vote_challenge", "submit_vote"], "auth_by_step": {"create_draft": {"mode": "management_signature", "operation_id": "postApiV1PollsDrafts", "required_headers": ["Signature-Input", "Signature", "Content-Digest", "Idempotency-Key"]}, "confirm_draft": {"mode": "management_signature", "operation_id": "postApiV1PollConfirm", "required_headers": ["Signature-Input", "Signature", "Content-Digest", "Idempotency-Key"]}, "publish_poll": {"mode": "management_signature", "operation_id": "postApiV1PollPublish", "required_headers": ["Signature-Input", "Signature", "Content-Digest", "Idempotency-Key"]}, "mint_link": {"mode": "management_signature", "operation_id": "postApiV1PollMintLink", "required_headers": ["Signature-Input", "Signature", "Content-Digest", "Idempotency-Key"]}, "get_vote_challenge": {"mode": "vote_bearer", "operation_id": "getApiV1PollVote", "required_headers": ["Authorization"]}, "submit_vote": {"mode": "vote_bearer_with_vote_proof", "operation_id": "postApiV1PollVote", "required_headers": ["Authorization", "Idempotency-Key"]}}, "links": {"api_catalog": "/.well-known/api-catalog", "openapi": "/openapi/openapi.yaml", "http_examples": "/openapi/components/examples/http/index.json", "signature_helper": "/openapi/components/examples/http/snippets/python/signature_primitives.py", "vote_proof_helper": "/openapi/components/examples/http/snippets/python/vote_proof_primitives.py", "feedback_proof_helper": "/openapi/components/examples/http/snippets/python/feedback_proof_primitives.py", "management_create_success_signed": "/openapi/components/examples/http/cases/mgt_create_success_signed.json", "feedback_create_management_reference": "/openapi/components/examples/http/cases/mgt_feedback_post_create_success_signed.json", "feedback_replace_management_reference": "/openapi/components/examples/http/cases/mgt_feedback_put_replace_success_signed.json", "feedback_create_vote_reference": "/openapi/components/examples/http/cases/vote_feedback_post_create_success_bearer.json", "feedback_replace_vote_reference": "/openapi/components/examples/http/cases/vote_feedback_put_replace_success_bearer.json", "vote_proof_construction_reference": "/openapi/components/examples/http/cases/vote_post_success_bearer_pop.json", "vote_proof_failure_reference": "/openapi/components/examples/http/cases/vote_post_conflict_invalid_submit_challenge.json"}, "first_contact_order": ["/", "/.well-known/api-catalog", "/openapi/openapi.yaml", "/openapi/components/examples/http/index.json"], "auth_lanes": {"management_signature": "Use RFC 9421 Signature-Input/Signature; include Content-Digest for management write operations.", "vote_bearer": "Use Authorization: Bearer <vote token>; call GET /api/v1/polls/{poll_id}/vote before POST /vote."}, "management_key_rules": {"create_requires_pre_registration": false, "create_key_binding": "POST /api/v1/polls/drafts binds signer identity from the same request: keyid must fingerprint owner.public_key.", "existing_poll_requires_active_owner_key": "For existing-poll management requests, keyid must already be an active owner key for the target poll.", "example_key_policy": "Published example keys are reproducible fixtures and may be blocked with reserved_management_key."}, "vote_submit_rules": {"flow": ["GET /api/v1/polls/{poll_id}/vote with bearer token.", "Build canonical response_hash from request.response JSON.", "Sign canonical vote-proof statement using vote_id + challenge_nonce from GET /vote.", "POST /api/v1/polls/{poll_id}/vote with proof and Idempotency-Key."], "canonical_statement_helper": "/openapi/components/examples/http/snippets/python/vote_proof_primitives.py"}, "feedback_submit_rules": {"flow": ["Choose exactly one lane: management signature OR vote bearer.", "POST /api/v1/feedback creates the first lane-scoped feedback record.", "PUT /api/v1/feedback replaces existing lane-scoped feedback, fails with feedback_not_found when scope is missing, and fails with feedback_not_editable when the existing record is no longer editable.", "Feedback context and evidence are flat bounded metadata maps; nested objects and arrays are rejected; values must be string, boolean, or null.", "For vote lane, build canonical feedback_hash from feedback request fields excluding proof.", "Sign canonical feedback-proof statement over poll_id, vote_id, feedback_hash, and issued_at.", "For vote lane, both POST and PUT require vote_id + proof and Idempotency-Key.", "v0 singleton limits: one management-lane feedback per poll and one vote-lane feedback per vote_id."], "canonical_statement_helper": "/openapi/components/examples/http/snippets/python/feedback_proof_primitives.py"}, "common_failures": {"reserved_management_key": {"when": "Management key is reserved for published examples.", "recovery": "Use a non-example management key, or enable reserved-example mode only in controlled dev/test."}, "invalid_http_signature": {"when": "HTTP signature header or signature value is invalid.", "recovery": "Rebuild signature input/signature from canonical request fields and retry."}, "http_signature_digest_mismatch": {"when": "Content-Digest does not match request body bytes.", "recovery": "Recompute Content-Digest from exact request body bytes and retry."}, "stale_http_signature": {"when": "HTTP signature timestamp is stale outside allowed skew window.", "recovery": "Regenerate signature with current UTC timestamps and retry."}, "future_http_signature": {"when": "HTTP signature timestamp is in the future outside allowed skew window.", "recovery": "Synchronize clock, regenerate signature, and retry."}, "invalid_submit_challenge": {"when": "Submit challenge token is invalid.", "recovery": "Fetch a fresh challenge via GET /api/v1/polls/{poll_id}/vote and retry."}, "expired_submit_challenge": {"when": "Submit challenge token is expired.", "recovery": "Fetch a fresh challenge via GET /api/v1/polls/{poll_id}/vote and retry."}, "vote_link_revoked": {"when": "Vote capability link is revoked.", "recovery": "Use another active capability link that has not been revoked."}, "vote_link_expired": {"when": "Vote capability link is expired.", "recovery": "Use another active capability link that has not expired."}, "vote_link_cap_exhausted": {"when": "Vote capability link has exhausted its usage cap.", "recovery": "Use another active capability link with remaining quota."}}, "example_refs": {"management_create_success_signed": "/openapi/components/examples/http/cases/mgt_create_success_signed.json", "feedback_create_management_reference": "/openapi/components/examples/http/cases/mgt_feedback_post_create_success_signed.json", "feedback_replace_management_reference": "/openapi/components/examples/http/cases/mgt_feedback_put_replace_success_signed.json", "feedback_create_vote_reference": "/openapi/components/examples/http/cases/vote_feedback_post_create_success_bearer.json", "feedback_replace_vote_reference": "/openapi/components/examples/http/cases/vote_feedback_put_replace_success_bearer.json", "vote_proof_construction_reference": "/openapi/components/examples/http/cases/vote_post_success_bearer_pop.json", "vote_proof_failure_reference": "/openapi/components/examples/http/cases/vote_post_conflict_invalid_submit_challenge.json"}, "next_request": {"step_id": "create_draft", "operation_id": "postApiV1PollsDrafts", "method": "POST", "path": "/api/v1/polls/drafts", "requires": {"required_headers": ["Content-Type", "Idempotency-Key", "Content-Digest", "Signature-Input", "Signature"], "idempotency_key_pattern": "^[A-Za-z0-9._~-]{20,200}$", "signature_components": ["@method", "@path", "@query", "content-digest", "idempotency-key"], "content_digest_required": true}, "body_schema_ref": "#/components/schemas/PollDraftCreateRequest", "body_minimal_example": {"owner": {"scheme": "ed25519_statement_v1", "public_key": "<base64url-ed25519-pubkey>"}, "context": {"title": "Example poll"}, "form_schema": {"dialect": "vote-form.v1", "questions": [{"id": "q1", "kind": "boolean", "title": "Approve?", "required": true}]}, "methods": [], "commitment": {"proof_mode": "capability_pop"}}}, "carry_forward": {"transitions": [{"from_step": "create_draft", "to_step": "confirm_draft", "required_from_response_fields": ["poll_id", "revision", "payload_hash"], "next_request_template": {"operation_id": "postApiV1PollConfirm", "method": "POST", "path": "/api/v1/polls/{poll_id}/confirm", "path_params": {"poll_id": "{from.create_draft.poll_id}"}, "body": {"if_match_revision": "{from.create_draft.revision}", "confirm_payload_hash": "{from.create_draft.payload_hash}"}}}, {"from_step": "confirm_draft", "to_step": "publish_poll", "required_from_response_fields": ["poll_id", "revision"], "next_request_template": {"operation_id": "postApiV1PollPublish", "method": "POST", "path": "/api/v1/polls/{poll_id}/publish", "path_params": {"poll_id": "{from.confirm_draft.poll_id}"}, "body": {"if_match_revision": "{from.confirm_draft.revision}"}}}, {"from_step": "publish_poll", "to_step": "mint_link", "required_from_response_fields": ["poll_id"], "next_request_template": {"operation_id": "postApiV1PollMintLink", "method": "POST", "path": "/api/v1/polls/{poll_id}/links", "path_params": {"poll_id": "{from.publish_poll.poll_id}"}}}, {"from_step": "mint_link", "to_step": "get_vote_challenge", "required_from_response_fields": ["poll_id", "vote_token"], "next_request_template": {"operation_id": "getApiV1PollVote", "method": "GET", "path": "/api/v1/polls/{poll_id}/vote", "path_params": {"poll_id": "{from.mint_link.poll_id}"}, "headers": {"Authorization": "Bearer {from.mint_link.vote_token}"}}}, {"from_step": "get_vote_challenge", "to_step": "submit_vote", "required_from_response_fields": ["poll_id", "submit.next_vote_id", "submit.challenge_nonce", "submit.submit_challenge_token"], "next_request_template": {"operation_id": "postApiV1PollVote", "method": "POST", "path": "/api/v1/polls/{poll_id}/vote", "path_params": {"poll_id": "{from.get_vote_challenge.poll_id}"}, "body": {"vote_id": "{from.get_vote_challenge.submit.next_vote_id}", "submit_challenge_token": "{from.get_vote_challenge.submit.submit_challenge_token}", "response": "<your response object>", "proof": {"scheme": "ed25519_statement_v1", "public_key": "<base64url-ed25519-pubkey>", "issued_at": "<RFC3339 UTC timestamp>", "signature": "<base64url-ed25519-signature>"}}}}]}, "response_contract": {"planning_in_discovery_only": true, "execution_hints_allowed": false, "protocol_ephemeral_handles_allowed": true, "protocol_ephemeral_handle_exceptions": ["submit.next_vote_id"]}, "workflow_constraints": {"close_poll": {"operation_id": "postApiV1PollClose", "required_status": "open", "allowed_close_reasons": ["completed", "open_expired"]}, "poll_results": {"operation_id": "getApiV1PollResults", "tallyable_states": [{"status": "closed"}, {"status": "final", "state_reason": "closed_expired"}], "error_code_if_not_tallyable": "poll_not_closed", "unsupported_methods_error_code": "aggregation_method_not_implemented", "recovery_hint": "Close the poll or wait for timeout progression to reach final/closed_expired before requesting results."}}, "lifecycle_next_moves": [{"from_status": "draft", "to_status": "open", "allowed_reasons": [], "trigger": "operation", "operation_id": "postApiV1PollPublish", "method": "POST", "path": "/api/v1/polls/{poll_id}/publish"}, {"from_status": "draft", "to_status": "final", "allowed_reasons": ["discarded"], "trigger": "operation", "operation_id": "postApiV1PollDiscard", "method": "POST", "path": "/api/v1/polls/{poll_id}/discard"}, {"from_status": "draft", "to_status": "final", "allowed_reasons": ["draft_expired"], "trigger": "automatic"}, {"from_status": "open", "to_status": "closed", "allowed_reasons": ["completed"], "trigger": "operation", "operation_id": "postApiV1PollClose", "method": "POST", "path": "/api/v1/polls/{poll_id}/close", "reason_field": "state_reason"}, {"from_status": "open", "to_status": "closed", "allowed_reasons": ["open_expired"], "trigger": "automatic"}, {"from_status": "open", "to_status": "final", "allowed_reasons": ["canceled", "aborted"], "trigger": "operation", "operation_id": "postApiV1PollFinalize", "method": "POST", "path": "/api/v1/polls/{poll_id}/finalize", "reason_field": "state_reason"}, {"from_status": "closed", "to_status": "final", "allowed_reasons": ["closed_expired"], "trigger": "automatic"}], "data_retention": {"policy_version": "v1", "classes": {"poll_lifecycle": {"auto_delete": true, "retention_mode": "state_deadline_timeout_then_finalized_delete_after_cleanup", "draft_timeout_seconds": 900, "open_timeout_seconds": 3600, "closed_timeout_seconds": 600, "finalized_retention_seconds": 604800, "timeout_progression_command": "run_timeout_progression_cycle", "cleanup_command": "run_cleanup_cycle"}, "vote_records": {"auto_delete": true, "retention_mode": "finalized_poll_bundle_cleanup", "finalized_retention_seconds": 604800, "cleanup_command": "run_cleanup_cycle"}, "feedback_records": {"auto_delete": true, "retention_mode": "finalized_poll_bundle_cleanup", "finalized_retention_seconds": 604800, "cleanup_command": "run_cleanup_cycle"}, "vote_links": {"credential_expiry_field": "expires_at", "auto_delete": true, "retention_mode": "delete_after_cleanup_unused_or_finalized_bundle", "unused_delete_after_grace_seconds": 86400, "finalized_bundle_retention_seconds": 604800, "cleanup_command": "run_cleanup_cycle"}, "management_nonces": {"auto_delete": true, "retention_mode": "delete_after_cleanup", "retention_seconds": 86400, "cleanup_command": "run_cleanup_cycle"}, "idempotency_records": {"auto_delete": true, "retention_mode": "delete_after_cleanup", "retention_seconds": 900, "cleanup_command": "run_cleanup_cycle"}, "management_key_policies": {"auto_delete": true, "retention_mode": "lifted_policy_delete_after_cleanup", "retention_seconds": 7776000, "cleanup_command": "run_cleanup_cycle"}}}, "advanced_refs": {"complex_schema_walkthrough": "/openapi/components/examples/http/snippets/python/localhost_complex_schema_walkthrough.py", "results_complex_success_case": "/openapi/components/examples/http/cases/mgt_results_success_signed_complex_schema.json", "results_poll_not_closed_case": "/openapi/components/examples/http/cases/mgt_results_conflict_poll_not_closed.json", "close_invalid_reason_case": "/openapi/components/examples/http/cases/mgt_close_validation_invalid_close_reason.json"}}