{"components":{"headers":{"Commitscale-Web-Build":{"description":"The build of the web app this server serves now: 16 hex digits of the SHA-256 of its page, so any app update alters it. The web app compares it with the build it loaded, and says when a new version is out. A server built without its web app leaves it out.","schema":{"examples":["5e0c2f9a7b14d863"],"pattern":"^[0-9a-f]{16}$","type":"string"}},"Idempotent-Replayed":{"description":"`true` when this is the stored response to an earlier request with the same idempotency key.","schema":{"enum":["true"],"type":"string"}},"RateLimit":{"description":"What is left of the caller's budget: `\"default\";r=<requests left>;t=<seconds until it is full again>`.","schema":{"examples":["\"default\";r=598;t=1"],"type":"string"}},"RateLimit-Policy":{"description":"The caller's budget: `\"default\";q=<requests>;w=<seconds>`.","schema":{"examples":["\"default\";q=600;w=60"],"type":"string"}},"Retry-After":{"description":"Seconds to wait before trying again.","schema":{"minimum":0,"type":"integer"}}},"parameters":{"BillingIdempotencyKey":{"description":"A durable billing request key, scoped to the person or organization. Retry uncertain results with the same key and input. Use a fresh key for new work after a final refusal. Plan deliveries scope keys to service, workspace kind and ID. Records do not expire.","example":"019a1f40-7c00-7000-8000-000000000003","in":"header","name":"Idempotency-Key","required":true,"schema":{"maxLength":128,"minLength":1,"type":"string"}},"IdempotencyKey":{"description":"Makes a retry safe: a request with the same key and the same method, path, and body as an earlier one gets the earlier response instead of doing the work again. 1 to 255 visible ASCII characters; use a new key for each new request, such as a UUID. Keys are kept per caller for a day, and ignored for requests without credentials.","example":"019a1f40-7c00-7000-8000-000000000003","in":"header","name":"Idempotency-Key","required":false,"schema":{"maxLength":255,"minLength":1,"type":"string"}},"TestModelIdempotencyKey":{"description":"Required for model checks and questions. Reuse the exact body and IDs on retries. Keys are scoped to the caller's namespace and retained as hashes after the 24-hour replay window. Expired identities return model_replay_expired and cannot count again.","example":"019a1f40-7c00-7000-8000-000000000003","in":"header","name":"Idempotency-Key","required":true,"schema":{"maxLength":255,"minLength":1,"type":"string"}}},"responses":{"BadRequest":{"content":{"application/problem+json":{"example":{"code":"invalid_request","status":400,"title":"The request cannot go on: a login or link expired or was denied, or the request is malformed.","type":"https://commitscale.com/errors/invalid-request"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"The request cannot go on: a login or link expired or was denied, or the request is malformed.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"Busy":{"content":{"application/problem+json":{"example":{"code":"unavailable","status":503,"title":"The server is busy, a workspace is locked, or an idempotent write takes too long and continues.","type":"https://commitscale.com/errors/unavailable"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"The server is busy, a workspace is locked, or an idempotent write takes too long and continues.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"Conflict":{"content":{"application/problem+json":{"example":{"code":"conflict","status":409,"title":"The request conflicts with the current state, or with a request that used the same idempotency key.","type":"https://commitscale.com/errors/conflict"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"The request conflicts with the current state, or with a request that used the same idempotency key.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"Forbidden":{"content":{"application/problem+json":{"example":{"code":"forbidden","status":403,"title":"The caller may not do this.","type":"https://commitscale.com/errors/forbidden"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"The caller may not do this.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"Internal":{"content":{"application/problem+json":{"example":{"code":"internal","status":500,"title":"The server failed, and logged why.","type":"https://commitscale.com/errors/internal"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"The server failed, and logged why.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"InvalidRequest":{"content":{"application/problem+json":{"example":{"code":"invalid_request","status":422,"title":"The request breaks a rule, or reuses an idempotency key for a different request.","type":"https://commitscale.com/errors/invalid-request"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"The request breaks a rule, or reuses an idempotency key for a different request.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"MethodNotAllowed":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"This method is not supported.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"NotFound":{"content":{"application/problem+json":{"example":{"code":"not_found","status":404,"title":"It does not exist, or the caller cannot read it.","type":"https://commitscale.com/errors/not-found"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"It does not exist, or the caller cannot read it.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"ReplayExpired":{"content":{"application/problem+json":{"example":{"code":"model_replay_expired","status":410,"title":"The replay window expired; its identity cannot run or count again.","type":"https://commitscale.com/errors/model-replay-expired"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"The replay window expired; its identity cannot run or count again.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"TooLarge":{"content":{"application/problem+json":{"example":{"code":"invalid_request","status":413,"title":"The request exceeds an input bound.","type":"https://commitscale.com/errors/invalid-request"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"The request exceeds an input bound.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"TooManyRequests":{"content":{"application/problem+json":{"example":{"code":"quota_exceeded","status":429,"title":"The caller used its requests for this minute.","type":"https://commitscale.com/errors/quota-exceeded"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"The caller used its requests for this minute.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"Unauthenticated":{"content":{"application/problem+json":{"example":{"code":"unauthenticated","status":401,"title":"No credentials were sent, or the token was not accepted.","type":"https://commitscale.com/errors/unauthenticated"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"No credentials were sent, or the token was not accepted.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"Unavailable":{"content":{"application/problem+json":{"example":{"code":"unavailable","status":502,"title":"A service the server needs did not answer.","type":"https://commitscale.com/errors/unavailable"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"A service the server needs did not answer.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"UnsupportedMediaType":{"content":{"application/problem+json":{"example":{"code":"invalid_request","status":415,"title":"Send the expected content type.","type":"https://commitscale.com/errors/invalid-request"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Send the expected content type.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}}},"schemas":{"Account":{"properties":{"email":{"description":"The person's sign-in email address.","example":"person@example.com","type":"string"},"name":{"description":"The account's public username.","example":"alex","type":"string"}},"required":["name","email"],"type":"object"},"Actor":{"description":"Who is making a request.","properties":{"handle":{"description":"The caller's handle, such as user:alex.","example":"user:alex","type":"string"},"is_admin":{"description":"Whether this person has server management rights. Delegated credentials do not inherit them.","type":"boolean"},"kind":{"$ref":"#/components/schemas/ActorKind","description":"Whether the caller is a person, an agent or a runner."},"name":{"description":"The caller's user, agent or runner name.","example":"alex","type":"string"},"owner":{"description":"The owning person, for an agent.","type":"string"},"signed_in_with":{"description":"For a browser session: how it began.","enum":["cscale_web","email_link","passkey","oidc"],"type":"string"},"token_expires_at":{"description":"When the credential expires, in UTC. Omitted for a credential without an expiry.","format":"date-time","type":"string"}},"required":["handle","kind","name","is_admin"],"type":"object"},"ActorKind":{"enum":["user","agent","runner"],"type":"string"},"AgentSignupPoll":{"additionalProperties":false,"properties":{"signup_code":{"description":"Secret polling code returned by the sign-up start. Never use the email verification token here.","example":"example-signup-code","type":"string"}},"required":["signup_code"],"type":"object"},"AgentSignupPreview":{"description":"Read-only preview authenticated by the email link, never the polling code.","properties":{"data_sharing":{"$ref":"#/components/schemas/SignupDataSharing","description":"Optional consent choice for the person confirming a new Free account."},"email":{"description":"The person's sign-in email address.","example":"person@example.com","type":"string"},"key_name":{"description":"The person's label for this check key.","example":"My coding agent","type":"string"},"plan":{"description":"The effective plan name, such as free or pro.","example":"free","type":"string"}},"required":["email","key_name","plan"],"type":"object"},"AgentSignupRequest":{"additionalProperties":false,"properties":{"email":{"description":"The person's sign-in email address.","example":"person@example.com","type":"string"},"key_name":{"description":"The person's name for this agent's key, 1 to 60 plain characters.","example":"My coding agent","type":"string"},"plan":{"description":"Optional Pro checkout after verification. Omitted or `free` stays Free.","example":"free","type":"string"}},"required":["email","key_name"],"type":"object"},"AgentSignupStarted":{"properties":{"expires_in_s":{"description":"Seconds until this request expires. Start again after it expires.","example":900,"format":"int64","minimum":0,"type":"integer"},"interval_s":{"description":"Minimum seconds to wait between polls. Polling sooner returns slow_down.","example":5,"format":"int64","minimum":0,"type":"integer"},"message":{"description":"A message for the person using this client.","example":"Check your email to confirm your account.","type":"string"},"signup_code":{"description":"Secret for polling. Keep it private; it is not the email link.","example":"signup-polling-code","type":"string"}},"required":["signup_code","interval_s","expires_in_s","message"],"type":"object"},"AgentSignupVerified":{"properties":{"account":{"description":"The person's account name.","example":"alex","type":"string"},"key_name":{"description":"The person's label for this check key.","example":"My coding agent","type":"string"}},"required":["account","key_name"],"type":"object"},"AgentSignupVerify":{"additionalProperties":false,"properties":{"data_sharing":{"$ref":"#/components/schemas/SetDataSharing","description":"The person's choice on the confirmation page. Omission leaves sharing off."},"token":{"description":"Only the email's fragment carries this secret.","example":"email-fragment-token","type":"string"}},"required":["token"],"type":"object"},"AskTestQuestion":{"additionalProperties":false,"properties":{"candidate_id":{"description":"Opaque candidate identifier, never a path or excerpt.","example":"candidate:1","type":"string"},"input":{"$ref":"#/components/schemas/TestModelInput","description":"Input for this question. Its kind must match the question; excerpts must already have sensitive values removed."},"prompt_version":{"description":"Prompt layout version. Send v17-layout-1.","example":"v17-layout-1","type":"string"},"question":{"$ref":"#/components/schemas/TestModelQuestion","description":"One of the five questions: behaviour, test_code, test_edit, build or caught."},"redaction_version":{"description":"Version of the client's secret-scrubbing rules. The server scrubs the input again.","example":"1","type":"string"},"request_id":{"description":"Random UUID for this question, reused for exact retries.","example":"019a1f40-7c00-7000-8000-000000000002","format":"uuid","type":"string"}},"required":["request_id","candidate_id","question","prompt_version","input","redaction_version"],"type":"object"},"AuthorizationServerMetadata":{"properties":{"authorization_endpoint":{"description":"Where a client begins authorization.","type":"string"},"authorization_response_iss_parameter_supported":{"description":"Whether authorization responses include the issuer.","type":"boolean"},"client_id_metadata_document_supported":{"description":"Whether client identifiers may be metadata document URLs.","type":"boolean"},"code_challenge_methods_supported":{"description":"Supported PKCE challenge methods.","items":{"type":"string"},"type":"array"},"grant_types_supported":{"description":"Supported OAuth grant types.","items":{"type":"string"},"type":"array"},"issuer":{"description":"The authorization server's issuer URL.","type":"string"},"registration_endpoint":{"description":"Where a public client registers its redirects.","type":"string"},"response_modes_supported":{"description":"Supported authorization response modes.","items":{"type":"string"},"type":"array"},"response_types_supported":{"description":"Supported authorization response types.","items":{"type":"string"},"type":"array"},"revocation_endpoint":{"description":"Where a client revokes a token.","type":"string"},"revocation_endpoint_auth_methods_supported":{"description":"Supported client authentication at the revocation endpoint.","items":{"type":"string"},"type":"array"},"scopes_supported":{"description":"Supported scope names.","items":{"type":"string"},"type":"array"},"token_endpoint":{"description":"Where a client exchanges or refreshes a token.","type":"string"},"token_endpoint_auth_methods_supported":{"description":"Supported client authentication at the token endpoint.","items":{"type":"string"},"type":"array"}},"required":["issuer","authorization_endpoint","token_endpoint","registration_endpoint","revocation_endpoint","response_types_supported","response_modes_supported","grant_types_supported","code_challenge_methods_supported","token_endpoint_auth_methods_supported","revocation_endpoint_auth_methods_supported","scopes_supported","client_id_metadata_document_supported","authorization_response_iss_parameter_supported"],"type":"object"},"BillingLink":{"properties":{"url":{"description":"Absolute URL to open for the next step.","example":"https://billing.commitscale.com/checkout","type":"string"}},"required":["url"],"type":"object"},"BillingResolve":{"additionalProperties":false,"properties":{"key":{"description":"Idempotency-Key from the billing request whose outcome is uncertain.","example":"019a1f40-7c00-7000-8000-000000000003","type":"string"}},"required":["key"],"type":"object"},"BillingResolved":{"properties":{"key":{"description":"The billing request key that was looked up.","example":"019a1f40-7c00-7000-8000-000000000003","type":"string"},"resolved":{"description":"Whether the billing service has a final response for this request key.","type":"boolean"},"status":{"description":"HTTP status of the original billing request.","format":"int32","minimum":0,"type":"integer"}},"required":["key","resolved","status"],"type":"object"},"BillingSeats":{"additionalProperties":false,"properties":{"seats":{"description":"Exactly one seat for a personal subscription.","example":1,"format":"int32","type":"integer"}},"required":["seats"],"type":"object"},"BillingStatus":{"properties":{"owns_subscription":{"description":"Whether this person owns the paid subscription.","type":"boolean"},"plan":{"description":"The effective plan name, such as free or pro.","example":"free","type":"string"},"redirect_hosts":{"description":"Exact payment hosts configured by this server.","example":["billing.commitscale.com"],"items":{"type":"string"},"type":"array"},"subscription":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/BillingSubscription","description":"The current subscription, or null when there is none."}]}},"required":["plan","owns_subscription","redirect_hosts","subscription"],"type":"object"},"BillingSubscription":{"properties":{"cancel_at_period_end":{"description":"Whether renewal is cancelled at the end of this period.","type":"boolean"},"id":{"description":"Opaque billing subscription identifier.","example":"sub_019a1f40","type":"string"},"period_end":{"description":"End of the billing period as Unix seconds, or null when unavailable.","format":"int64","type":["integer","null"]},"shape_problems":{"description":"Subscription consistency issues reported by billing.","example":[],"items":{"type":"string"},"type":"array"},"status":{"description":"Subscription state reported by the billing service.","example":"active","type":"string"}},"required":["id","status","shape_problems","period_end","cancel_at_period_end"],"type":"object"},"CheckKey":{"properties":{"agent":{"description":"Handle of the check-only agent using this key.","example":"agent:check-1@alex","type":"string"},"created_at":{"description":"When this record was created, in UTC.","format":"date-time","type":"string"},"expires_at":{"description":"Empty until the agent collects the key.","format":"date-time","type":["string","null"]},"id":{"description":"Numeric check-key identifier used for renaming and revocation.","format":"int64","type":"integer"},"last_used_at":{"description":"Recorded at most once a minute, as for other tokens.","format":"date-time","type":["string","null"]},"name":{"description":"The person's label for this key, between 1 and 60 plain characters.","example":"My coding agent","type":"string"},"revoked_at":{"description":"When the key was revoked, in UTC; null while it has not been revoked.","format":"date-time","type":["string","null"]}},"required":["id","name","agent","created_at","expires_at","last_used_at","revoked_at"],"type":"object"},"CheckKeyIssued":{"properties":{"key":{"$ref":"#/components/schemas/CheckKey","description":"Metadata for the newly issued key. The separate token field is shown once."},"token":{"description":"Shown once. It cannot create or refresh credentials.","example":"cs_chk_example","type":"string"}},"required":["token","key"],"type":"object"},"CheckKeyList":{"properties":{"keys":{"description":"The caller's check keys, including revoked keys.","items":{"$ref":"#/components/schemas/CheckKey"},"type":"array"}},"required":["keys"],"type":"object"},"CheckKeyUsage":{"properties":{"days":{"description":"Daily usage, newest UTC date first. Dates without use have no entry.","items":{"$ref":"#/components/schemas/CheckKeyUsageDay"},"type":"array"},"key_id":{"description":"Identifier of the check key supplied in Authorization.","format":"int64","type":"integer"}},"required":["key_id","days"],"type":"object"},"CheckKeyUsageDay":{"properties":{"api_requests":{"description":"Requests admitted to the key's allowlist, including failed requests. These are not model tokens, successful checks, or billing units.","format":"int64","type":"integer"},"day":{"description":"UTC calendar date in YYYY-MM-DD form.","format":"date","type":"string"}},"required":["day","api_requests"],"type":"object"},"Confirmation":{"properties":{"action":{"description":"The protected action being confirmed.","example":"create_check_key","type":"string"},"agent":{"description":"The agent requesting this action, when applicable.","type":"string"},"confirmed_at":{"description":"When the person confirmed, in UTC; null while awaiting confirmation.","format":"date-time","type":["string","null"]},"consent":{"$ref":"#/components/schemas/Consent","description":"The question and choice the person is being asked to confirm."},"created_at":{"description":"When it was asked. Read leniently, so a client still reads a server from before it.","format":"date-time","type":"string"},"declined_at":{"description":"When its owner declined it. Declining ends it, so it expired then too.","format":"date-time","type":"string"},"details":{"description":"Separately labelled fields to read before confirming with a passkey.","items":{"$ref":"#/components/schemas/ConfirmationDetail"},"type":"array"},"expires_at":{"description":"When this credential or request expires, in UTC.","format":"date-time","type":"string"},"id":{"description":"Opaque confirmation identifier. Use its URL for the person's review.","example":"019a1f40-7c00-7000-8000-000000000004","type":"string"},"item":{"description":"Public name of the item the confirmation concerns.","type":"string"},"person":{"description":"Handle of the person who must confirm.","example":"user:alex","type":"string"},"reason":{"description":"The reason for the proposed action.","type":"string"},"repository":{"description":"The repository this action concerns, when applicable.","type":"string"},"title":{"description":"Short title for the person's confirmation page.","example":"Create a check key","type":"string"},"url":{"description":"Absolute URL to open for the next step.","example":"https://commitscale.com/confirm/019a1f40-7c00-7000-8000-000000000004","type":"string"}},"required":["id","url","title","person","action","created_at","expires_at","confirmed_at"],"type":"object"},"ConfirmationDetail":{"properties":{"label":{"description":"Label of a fact shown during confirmation.","example":"Key name","type":"string"},"value":{"description":"Value shown for that fact.","example":"Work laptop","type":"string"}},"required":["label","value"],"type":"object"},"ConnectedApp":{"properties":{"client_host":{"description":"The connected client's host.","type":"string"},"client_name":{"description":"The client's self-declared name, or null when it did not give one.","type":["string","null"]},"created_at":{"description":"When the person approved the connection.","format":"date-time","type":"string"},"id":{"description":"The connection's identifier.","format":"int64","type":"integer"},"last_used_at":{"description":"When the connection was last used, or null if it has not been used.","format":"date-time","type":["string","null"]},"scopes":{"description":"The approved OAuth scopes.","items":{"type":"string"},"type":"array"}},"required":["id","client_host","client_name","scopes","created_at","last_used_at"],"type":"object"},"ConnectedApps":{"properties":{"grants":{"description":"The person's active connections, newest first.","items":{"$ref":"#/components/schemas/ConnectedApp"},"type":"array"}},"required":["grants"],"type":"object"},"Consent":{"additionalProperties":false,"description":"The agent reports these words. They are masked before being stored.","properties":{"answer":{"description":"The answer the person is choosing.","example":"No","type":"string"},"question":{"description":"The exact question presented to the person.","example":"Share scrubbed check inputs to help improve Commitscale?","type":"string"}},"required":["question","answer"],"type":"object"},"CreateInviteRequest":{"description":"An invite to send.","properties":{"early_access":{"description":"The early-access request it answers, which is then marked invited.","format":"int64","type":"integer"},"email":{"description":"The person's sign-in email address.","example":"person@example.com","type":"string"},"org":{"description":"The organization to invite the person to, such as acme.","example":"acme","type":"string"},"role":{"$ref":"#/components/schemas/OrgRole","description":"The role it gives in the organization: a member, unless it says owner."}},"required":["email"],"type":"object"},"CreateOrgRequest":{"properties":{"name":{"description":"For example `acme`.","example":"acme","type":"string"}},"required":["name"],"type":"object"},"DataSharing":{"properties":{"current_wording_version":{"description":"Version of the consent text currently offered by this server.","example":"free-excerpts-2","type":"string"},"opted_in":{"description":"Whether the person explicitly chose to share scrubbed Free-plan check data.","type":"boolean"},"opted_in_at":{"description":"When sharing was enabled, in UTC; null if never enabled.","format":"date-time","type":["string","null"]},"plan":{"description":"The person's own plan, independent of any selected organization.","example":"free","type":"string"},"withdrawn_at":{"description":"When sharing was withdrawn, in UTC; null if never withdrawn.","format":"date-time","type":["string","null"]},"wording":{"description":"The exact consent text to show before asking the person to choose.","example":"Help us improve Commitscale by sharing data from your checks, with secrets removed.","type":"string"},"wording_version":{"description":"The wording last accepted by this person, if any.","example":"free-excerpts-2","type":["string","null"]}},"required":["plan","opted_in","opted_in_at","withdrawn_at","wording_version","current_wording_version","wording"],"type":"object"},"DeviceCodeRequest":{"description":"A code that `cscale auth login` shows, typed on the device page.","properties":{"approve":{"description":"Approve or deny; absent to only look the login up.","type":"boolean"},"user_code":{"description":"Short code shown in the terminal for the person to approve on the device page.","example":"BCDF-GHJK","type":"string"}},"required":["user_code"],"type":"object"},"DeviceLogin":{"description":"A terminal's login, as the device page shows it.","properties":{"client_name":{"description":"The application name shown to the person approving the sign-in.","example":"cscale","type":"string"},"expires_at":{"description":"When this credential or request expires, in UTC.","format":"date-time","type":"string"},"requested_at":{"description":"When the terminal asked.","format":"date-time","type":"string"},"requested_from":{"description":"The IP address the terminal asked from, as the server saw it, so a person can tell their own terminal's request.","type":"string"},"status":{"description":"Where the login stands.","enum":["pending","approved","denied"],"type":"string"},"user_code":{"description":"Short code shown in the terminal for the person to approve on the device page.","example":"BCDF-GHJK","type":"string"}},"required":["user_code","requested_at","expires_at","status"],"type":"object"},"DeviceStartRequest":{"description":"Starts a login for a terminal. With an email, the approval link goes there; without one, the person approves on the web app's device page.","properties":{"client_name":{"description":"The application name shown to the person approving the sign-in.","example":"cscale","type":"string"},"email":{"description":"The person's sign-in email address.","example":"person@example.com","type":"string"}},"type":"object"},"DeviceStartResponse":{"properties":{"device_code":{"description":"Secret polling code returned when this device login started. Keep it private.","example":"device-polling-code","type":"string"},"expires_in_s":{"description":"Seconds until this request expires. Start again after it expires.","example":900,"format":"int64","minimum":0,"type":"integer"},"interval_s":{"description":"Minimum seconds to wait between polls. Polling sooner returns slow_down.","example":5,"format":"int64","minimum":0,"type":"integer"},"message":{"description":"A message for the person using this client.","example":"Open the verification link and enter your code.","type":"string"},"user_code":{"description":"Short code shown in the terminal for the person to approve on the device page.","example":"BCDF-GHJK","type":"string"},"verification_uri":{"description":"The page to approve on, for a login without an email.","type":"string"}},"required":["device_code","user_code","interval_s","expires_in_s","message"],"type":"object"},"DeviceTokenRequest":{"properties":{"device_code":{"description":"Secret polling code returned when this device login started. Keep it private.","example":"example-device-code","type":"string"}},"required":["device_code"],"type":"object"},"EmailLinkRequest":{"properties":{"email":{"description":"The person's sign-in email address.","example":"person@example.com","type":"string"},"next":{"description":"The page to open after signing in.","type":"string"}},"required":["email"],"type":"object"},"EmailLinkSent":{"description":"The same answer whether or not an account uses the address.","properties":{"expires_in_s":{"description":"Seconds until this request expires. Start again after it expires.","example":900,"format":"int64","minimum":0,"type":"integer"},"message":{"description":"A message for the person using this client.","example":"Check your email to finish sign-in.","type":"string"}},"required":["message","expires_in_s"],"type":"object"},"Envelope":{"description":"A DSSE envelope, as the DSSE specification defines it.","properties":{"payload":{"description":"The statement's JSON, in standard base64.","example":"eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEifQ==","type":"string"},"payloadType":{"description":"DSSE payload type: application/vnd.in-toto+json.","example":"application/vnd.in-toto+json","type":"string"},"signatures":{"description":"DSSE signatures over the pre-authentication encoding of this payload.","items":{"$ref":"#/components/schemas/EnvelopeSignature"},"type":"array"}},"required":["payloadType","payload","signatures"],"type":"object"},"EnvelopeSignature":{"properties":{"keyid":{"description":"Identifier of the verification key. Pin it with the server and person you trust.","example":"acceptance-2026-10","type":"string"},"sig":{"description":"An Ed25519 signature over the envelope's PAE encoding, in base64.","example":"AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAhIiMkJSYnKCkqKywtLi8wMTIzNDU2Nzg5Ojs8PT4/QA==","type":"string"}},"required":["keyid","sig"],"type":"object"},"IdentityInfo":{"description":"An account at a provider, linked to the caller.","properties":{"created_at":{"description":"When this record was created, in UTC.","format":"date-time","type":"string"},"email":{"description":"The person's sign-in email address.","example":"person@example.com","type":"string"},"id":{"description":"Identifier used when unlinking this identity.","format":"int64","type":"integer"},"last_used_at":{"description":"When this credential was last used, in UTC. Absent until its first use.","format":"date-time","type":"string"},"provider":{"$ref":"#/components/schemas/LoginProvider","description":"The sign-in provider to which this identity belongs."}},"required":["id","provider","created_at"],"type":"object"},"IdentityList":{"properties":{"identities":{"description":"Sign-in provider identities linked to the caller.","items":{"$ref":"#/components/schemas/IdentityInfo"},"type":"array"}},"required":["identities"],"type":"object"},"Invite":{"description":"An invite that waits for its person.","properties":{"created_at":{"description":"When this record was created, in UTC.","format":"date-time","type":"string"},"email":{"description":"The address it went to, for an invite sent to an address. An invite to someone by their handle never shows their address.","example":"person@example.com","type":"string"},"expires_at":{"description":"Its link works until then.","format":"date-time","type":"string"},"id":{"description":"Identifier used to accept, decline or withdraw the invitation.","format":"int64","type":"integer"},"invited_by":{"description":"Who sent it, such as `user:john`.","example":"user:alex","type":"string"},"org":{"description":"The organization's namespace name, such as acme.","example":"acme","type":"string"},"person":{"description":"The person it is for, such as `user:carol`, for an invite sent to them by their handle.","example":"user:alex","type":"string"},"role":{"$ref":"#/components/schemas/OrgRole","description":"The person's role in the organization: owner or member."}},"required":["id","invited_by","created_at","expires_at"],"type":"object"},"InviteList":{"properties":{"invites":{"description":"Invitations visible to the caller.","items":{"$ref":"#/components/schemas/Invite"},"type":"array"}},"required":["invites"],"type":"object"},"InviteTicket":{"description":"What an invite's link does, before it is used.","properties":{"account":{"description":"The account that already uses the address: only a browser signed in to it accepts. Without one, accepting creates an account, under a username the person picks.","type":"string"},"email":{"description":"The address it went to.","example":"person@example.com","type":"string"},"expires_at":{"description":"When this credential or request expires, in UTC.","format":"date-time","type":"string"},"invited_by":{"description":"Who sent it, such as `john`.","example":"alex","type":"string"},"org":{"description":"The organization's namespace name, such as acme.","example":"acme","type":"string"},"role":{"$ref":"#/components/schemas/OrgRole","description":"The person's role in the organization: owner or member."}},"required":["email","invited_by","expires_at"],"type":"object"},"LoginMethods":{"description":"The ways to sign in that this server has turned on.","properties":{"email_link":{"description":"Whether sign-in by email link is enabled.","type":"boolean"},"passkeys":{"description":"Whether sign-in with a passkey is enabled.","type":"boolean"},"providers":{"description":"Configured OpenID Connect sign-in providers.","items":{"$ref":"#/components/schemas/LoginProvider"},"type":"array"},"signup":{"$ref":"#/components/schemas/SignupMode","description":"Whether new accounts may sign up: open or closed."}},"required":["email_link","passkeys","providers","signup"],"type":"object"},"LoginProvider":{"description":"An OpenID Connect provider, as the login page shows it.","properties":{"id":{"description":"This server's name for it, such as `google`.","example":"google","type":"string"},"name":{"description":"What its button says, such as `Google`.","example":"Google","type":"string"}},"required":["id","name"],"type":"object"},"LoginToken":{"description":"One of the caller's own tokens that still works.","properties":{"created_at":{"description":"When this record was created, in UTC.","format":"date-time","type":"string"},"current":{"description":"Whether this request came with it.","type":"boolean"},"expires_at":{"description":"When this credential or request expires, in UTC.","format":"date-time","type":"string"},"id":{"description":"What to revoke it by.","format":"int64","type":"integer"},"kind":{"$ref":"#/components/schemas/LoginTokenKind","description":"Credential kind, such as a personal token or browser session."},"last_used_at":{"description":"When this credential was last used, in UTC. Absent until its first use.","format":"date-time","type":"string"},"revocable":{"description":"Whether this sign-in may revoke it: only one older than itself, so a fresh sign-in always wins. A browser opened with `cscale web` is as old as the CLI login it came from.","type":"boolean"},"signed_in_with":{"description":"For a browser, how it signed in: `email_link`, `passkey`, `oidc`, or `cscale_web`, a session that ends with the CLI login it came from.","type":"string"},"token":{"description":"Its first characters, such as `cs_pat_3f9a…`.","example":"cs_pat_3f9a…","type":"string"}},"required":["id","kind","token","created_at","expires_at","current","revocable"],"type":"object"},"LoginTokenKind":{"description":"What a token signs in.","enum":["cli","browser"],"type":"string"},"LoginTokenList":{"properties":{"tokens":{"description":"The person's sign-in credentials, without their secret values.","items":{"$ref":"#/components/schemas/LoginToken"},"type":"array"}},"required":["tokens"],"type":"object"},"LoginTokensRevoked":{"description":"What signing out everywhere else did.","properties":{"newer":{"description":"How many it left, because they are newer than the sign-in that asked. Sign in again to sign them out.","format":"int32","minimum":0,"type":"integer"},"revoked":{"description":"How many CLI logins, browser sessions, and phones it signed out.","format":"int32","minimum":0,"type":"integer"}},"required":["revoked","newer"],"type":"object"},"ModelCheckUsage":{"description":"The current UTC month's check allowance for a personal workspace.","properties":{"cap":{"description":"100 on Free; null on plans without a monthly cap.","example":100,"format":"int64","type":["integer","null"]},"resets_at":{"description":"The start of the next UTC calendar month.","example":"2026-11-01T00:00:00Z","format":"date-time","type":"string"},"used":{"description":"Checks this month in Free workspaces sharing the verified mailbox, or this personal workspace's checks when its plan is not Free. Retries do not count again.","example":12,"format":"int64","type":"integer"}},"required":["used","cap","resets_at"],"type":"object"},"NameCheckKey":{"additionalProperties":false,"properties":{"name":{"description":"Label for the key, between 1 and 60 plain characters.","example":"My coding agent","type":"string"}},"required":["name"],"type":"object"},"NamespacePlan":{"properties":{"billing_available":{"description":"Whether this server has a billing service configured.","type":"boolean"},"can_test_plan":{"description":"Reserved account capability; false for customer organizations.","type":"boolean"},"flags":{"additionalProperties":{"type":"boolean"},"description":"Unknown or absent flags must be treated as off by clients.","example":{"account_pages":true,"accounts":true,"billing_pages":true,"model_api":true,"plan_pages":true,"remote_mcp":true,"usage":true,"usage_pages":true},"propertyNames":{"type":"string"},"type":"object"},"frozen":{"description":"Whether a Free organization with several people is frozen.","type":"boolean"},"namespace":{"description":"The personal or organization namespace name.","example":"alex","type":"string"},"people":{"description":"Number of people in this workspace; agents do not count.","format":"int64","type":"integer"},"plan":{"description":"The account's plan: free or pro.","example":"free","type":"string"}},"required":["namespace","plan","flags","can_test_plan","frozen","people","billing_available"],"type":"object"},"Next":{"description":"What a client should do next.","properties":{"action":{"description":"Such as `retry`, `login`, `sign_in`, `fix_request`, `add_member`, or `stop`.","example":"sign_in","type":"string"},"after_s":{"description":"For `retry`: how many seconds to wait first.","format":"int64","minimum":0,"type":"integer"},"command":{"description":"A command that does it, such as `cscale auth login`.","type":"string"},"url":{"description":"A page to open, such as a passkey confirmation.","type":"string"}},"required":["action"],"type":"object"},"OAuthError":{"properties":{"error":{"description":"The OAuth error code.","type":"string"},"error_description":{"description":"A short explanation for the client.","type":"string"}},"required":["error","error_description"],"type":"object"},"OAuthTokenResponse":{"properties":{"access_token":{"description":"The access token for this server's MCP resource.","type":"string"},"expires_in":{"description":"Seconds until the access token expires.","format":"int32","minimum":0,"type":"integer"},"refresh_token":{"description":"The refresh token, rotated each time it is used.","type":"string"},"scope":{"description":"Granted scopes separated by spaces.","type":"string"},"token_type":{"description":"The token type, Bearer.","type":"string"}},"required":["access_token","token_type","expires_in","refresh_token","scope"],"type":"object"},"Org":{"description":"An organization and its members.","properties":{"created_at":{"description":"When this record was created, in UTC.","format":"date-time","type":"string"},"members":{"description":"Current members, including their role or seat details.","items":{"$ref":"#/components/schemas/OrgMember"},"type":"array"},"name":{"description":"Organization namespace name.","example":"acme","type":"string"},"role":{"$ref":"#/components/schemas/OrgRole","description":"The caller's role in this organization."},"settings":{"additionalProperties":true,"description":"Reserved organization options.","example":{},"type":"object"},"teams":{"description":"Reserved membership groups.","example":[],"items":{"$ref":"#/components/schemas/Team"},"type":"array"}},"required":["settings","name","members","teams","created_at"],"type":"object"},"OrgList":{"properties":{"orgs":{"description":"Organizations this caller belongs to.","items":{"$ref":"#/components/schemas/OrgSummary"},"type":"array"}},"required":["orgs"],"type":"object"},"OrgMember":{"properties":{"added_at":{"description":"When the person became a member, in UTC.","format":"date-time","type":"string"},"handle":{"description":"`user:bob`.","example":"user:sam","type":"string"},"name":{"description":"The member's public username.","example":"sam","type":"string"},"role":{"$ref":"#/components/schemas/OrgRole","description":"The person's role in the organization: owner or member."},"teams":{"description":"Reserved membership groups.","example":[],"items":{"type":"string"},"type":"array"}},"required":["handle","name","role","teams","added_at"],"type":"object"},"OrgMemberList":{"properties":{"members":{"description":"Members of the organization and their roles.","items":{"$ref":"#/components/schemas/OrgMember"},"type":"array"}},"required":["members"],"type":"object"},"OrgRole":{"enum":["owner","member"],"type":"string"},"OrgSummary":{"description":"An organization that the caller belongs to.","properties":{"created_at":{"description":"When this record was created, in UTC.","format":"date-time","type":"string"},"name":{"description":"Organization namespace name.","example":"acme","type":"string"},"role":{"$ref":"#/components/schemas/OrgRole","description":"The caller's role in this organization."}},"required":["name","created_at"],"type":"object"},"OrganizationBilling":{"properties":{"buyer":{"description":"The buying person's handle, when the account still exists.","type":"string"},"buyer_is_you":{"description":"Whether the caller bought this subscription.","type":"boolean"},"buyer_left":{"description":"Whether the buying owner has since left or lost ownership.","type":"boolean"},"monthly_usd":{"description":"Current monthly price in US dollars at $20 per person.","format":"int64","type":"integer"},"plan":{"description":"Pro when the unblocked subscription is active, trialing, or past due; otherwise Free.","type":"string"},"redirect_hosts":{"description":"Payment hosts permitted by this server.","items":{"type":"string"},"type":"array"},"seated":{"description":"Handles currently covered by this organization's subscription.","items":{"type":"string"},"type":"array"},"seats":{"description":"Current people in the organization, including owners.","format":"int64","type":"integer"},"subscription":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/OrganizationSubscription","description":"The subscription, or null when none has been recorded."}]}},"required":["plan","seats","monthly_usd","seated","subscription","buyer_left","buyer_is_you","redirect_hosts"],"type":"object"},"OrganizationBillingSummary":{"properties":{"plan":{"description":"The organization's effective plan: `internal`, `pro`, or `free`.","type":"string"},"seats":{"description":"Current number of people in the organization.","format":"int64","type":"integer"}},"required":["plan","seats"],"type":"object"},"OrganizationCheckoutResolution":{"properties":{"pending":{"description":"Whether a checkout still occupies the organization.","type":"boolean"},"url":{"description":"An open checkout URL, only for the owner who started it.","type":"string"}},"required":["pending"],"type":"object"},"OrganizationSubscription":{"properties":{"cancel_at_period_end":{"description":"Whether cancellation is scheduled for the end of the paid period.","type":"boolean"},"period_end":{"description":"End of the paid period as Unix seconds.","format":"int64","type":"integer"},"shape_problems":{"description":"Configuration problems that need billing support.","items":{"type":"string"},"type":"array"},"status":{"description":"Current Stripe subscription status.","type":"string"}},"required":["status","period_end","cancel_at_period_end","shape_problems"],"type":"object"},"PasskeyAddRequest":{"description":"The browser's answer to a ceremony: a `PublicKeyCredential` in JSON. The browser's new credential, which adds the passkey.","properties":{"ceremony":{"description":"Opaque ID from the passkey challenge. Return it with the credential from that ceremony.","example":"019a1f40-7c00-7000-8000-000000000005","type":"string"},"credential":{"additionalProperties":true,"description":"The browser's WebAuthn PublicKeyCredential as JSON: id, rawId, type, response and clientExtensionResults. Binary fields in response use base64url. Send the credential returned by this ceremony; a credential from another ceremony cannot be reused.","type":"object"},"name":{"description":"Its name, when the start gave none. The web app names it after the password manager that saved it, such as `1Password`.","type":"string"}},"required":["ceremony","credential"],"type":"object"},"PasskeyChallenge":{"description":"A passkey ceremony for the browser: the options for `navigator.credentials`, as webauthn-rs writes them, and the ID that finishes it.","properties":{"ceremony":{"description":"Opaque ID from the passkey challenge. Return it with the credential from that ceremony.","example":"019a1f40-7c00-7000-8000-000000000005","type":"string"},"options":{"additionalProperties":true,"description":"Options for navigator.credentials.create or get. publicKey contains the WebAuthn challenge and relying-party settings; binary fields use base64url. Pass these options to the browser's credential API.","type":"object"}},"required":["ceremony","options"],"type":"object"},"PasskeyFinishRequest":{"properties":{"ceremony":{"description":"Opaque ID from the passkey challenge. Return it with the credential from that ceremony.","example":"019a1f40-7c00-7000-8000-000000000005","type":"string"},"credential":{"additionalProperties":true,"description":"The browser's WebAuthn PublicKeyCredential as JSON: id, rawId, type, response and clientExtensionResults. Binary fields in response use base64url. Send the credential returned by this ceremony; a credential from another ceremony cannot be reused.","type":"object"}},"required":["ceremony","credential"],"type":"object"},"PasskeyInfo":{"properties":{"created_at":{"description":"When this record was created, in UTC.","format":"date-time","type":"string"},"id":{"description":"Numeric identifier used when removing this passkey.","format":"int64","type":"integer"},"last_used_at":{"description":"When this credential was last used, in UTC. Absent until its first use.","format":"date-time","type":"string"},"name":{"description":"The person's name for this passkey.","example":"Work laptop","type":"string"}},"required":["id","name","created_at"],"type":"object"},"PasskeyLayer":{"properties":{"confirmation":{"$ref":"#/components/schemas/Confirmation","description":"Turning off waits for this passkey confirmation."},"enabled":{"description":"Whether the person requires passkey confirmation for protected check actions.","type":"boolean"}},"required":["enabled"],"type":"object"},"PasskeyList":{"properties":{"passkeys":{"description":"Passkeys registered to this person.","items":{"$ref":"#/components/schemas/PasskeyInfo"},"type":"array"}},"required":["passkeys"],"type":"object"},"PasskeyRegisterRequest":{"properties":{"name":{"description":"Its name, such as the device it is on. Without one, it takes the name its finish gives, or `Passkey`.","type":"string"}},"type":"object"},"Problem":{"description":"An error, as RFC 9457 Problem Details: every API error has this shape, as `application/problem+json`. Programs act on `code`; `title` and `detail` are for reading, and their words may change.","properties":{"billing_code":{"description":"Stable reason returned by the billing service when a billing request is refused.","type":"string"},"code":{"description":"A stable code, such as `not_found`. The API's description lists every code and what to do about it.","example":"not_found","type":"string"},"detail":{"description":"What went wrong this time, and often how to fix it.","type":"string"},"next":{"$ref":"#/components/schemas/Next","description":"The next useful action, including a retry delay or confirmation URL when needed."},"pending_key":{"description":"The original billing request key to resolve before starting new work.","type":"string"},"retry_after_s":{"description":"Wait this many seconds before retrying; `Retry-After` says the same.","format":"int64","minimum":0,"type":"integer"},"status":{"description":"The HTTP status.","format":"int32","minimum":0,"type":"integer"},"title":{"description":"A short summary, such as `Not found`.","example":"Not found","type":"string"},"type":{"description":"`https://commitscale.com/errors/` and the code, with hyphens, such as `https://commitscale.com/errors/not-found`.","example":"https://commitscale.com/errors/not-found","type":"string"}},"required":["type","title","status","code"],"type":"object"},"ProviderRedirect":{"description":"Where to send the browser: the provider's authorization page.","properties":{"url":{"description":"Absolute URL to open for the next step.","example":"https://accounts.google.com/o/oauth2/v2/auth","type":"string"}},"required":["url"],"type":"object"},"ProviderStartRequest":{"properties":{"next":{"description":"Local page to open after sign-in.","type":"string"}},"type":"object"},"Registered":{"properties":{"application_type":{"description":"Web or native.","type":"string"},"client_id":{"description":"The public client's identifier.","type":"string"},"client_id_issued_at":{"description":"When the identifier was issued, in Unix seconds.","format":"int64","type":"integer"},"client_name":{"description":"The client's self-declared name.","type":"string"},"grant_types":{"description":"The registered grant types.","items":{"type":"string"},"type":"array"},"redirect_uris":{"description":"The registered redirect URIs.","items":{"type":"string"},"type":"array"},"response_types":{"description":"The registered response types.","items":{"type":"string"},"type":"array"},"token_endpoint_auth_method":{"description":"Public clients use none.","type":"string"}},"required":["client_id","client_id_issued_at","redirect_uris","grant_types","response_types","token_endpoint_auth_method","client_name","application_type"],"type":"object"},"Registration":{"properties":{"application_type":{"description":"Web or native. Defaults to web.","type":"string"},"client_name":{"description":"Self-declared, at most 100 characters. No control, format, tag, separator or double-quote characters.","type":"string"},"grant_types":{"description":"Authorization code and optionally refresh token. Defaults to authorization code.","items":{"type":"string"},"type":"array"},"redirect_uris":{"description":"One to ten HTTPS or loopback HTTP redirect URIs.","items":{"type":"string"},"type":"array"},"response_types":{"description":"The supported response type is code.","items":{"type":"string"},"type":"array"},"token_endpoint_auth_method":{"description":"Public clients use none.","type":"string"}},"required":["redirect_uris"],"type":"object"},"Remote":{"properties":{"supportedProtocolVersions":{"description":"Protocol versions supported by this endpoint.","items":{"type":"string"},"type":"array"},"type":{"description":"The remote transport type.","type":"string"},"url":{"description":"The remote MCP resource URL.","type":"string"}},"required":["type","url","supportedProtocolVersions"],"type":"object"},"ResourceMetadata":{"properties":{"authorization_servers":{"description":"Authorization servers that issue tokens for this resource.","items":{"type":"string"},"type":"array"},"bearer_methods_supported":{"description":"Supported ways to send a bearer token.","items":{"type":"string"},"type":"array"},"resource":{"description":"The MCP resource URL.","type":"string"},"resource_name":{"description":"The resource's human-readable name.","type":"string"},"scopes_supported":{"description":"Scope names supported by this resource.","items":{"type":"string"},"type":"array"}},"required":["resource","authorization_servers","scopes_supported","bearer_methods_supported","resource_name"],"type":"object"},"ServerCard":{"properties":{"$schema":{"description":"The server-card schema URL.","type":"string"},"description":{"description":"A short description of the server.","type":"string"},"name":{"description":"The server's registered name.","type":"string"},"remotes":{"description":"The available remote transports.","items":{"$ref":"#/components/schemas/Remote"},"type":"array"},"title":{"description":"The server's human-readable title.","type":"string"},"version":{"description":"The server's version.","type":"string"}},"required":["$schema","name","title","description","version","remotes"],"type":"object"},"SetDataSharing":{"additionalProperties":false,"properties":{"opted_in":{"description":"Whether the person explicitly chose to share scrubbed Free-plan check data.","type":"boolean"},"wording_version":{"description":"Required and current when opting in. Withdrawal needs no version.","example":"free-excerpts-2","type":"string"}},"required":["opted_in"],"type":"object"},"SetMemberRequest":{"description":"Invites a person to an organization, or updates a member's role. Without a role, the invite is to be a member, and a member keeps theirs.","properties":{"role":{"$ref":"#/components/schemas/OrgRole","description":"The person's role in the organization: owner or member."}},"type":"object"},"SetPasskeyLayer":{"additionalProperties":false,"properties":{"enabled":{"description":"Whether the person requires passkey confirmation for protected check actions.","type":"boolean"}},"required":["enabled"],"type":"object"},"SignAcceptanceRequest":{"additionalProperties":false,"description":"The protected signature request. The payload and PAE must agree.","properties":{"by":{"description":"The signed-in person's handle, also present in the statement.","example":"user:alex","type":"string"},"keyid":{"description":"Identifier of the verification key. Pin it with the server and person you trust.","example":"acceptance-2026-10","type":"string"},"pae":{"description":"DSSE pre-authentication encoding of those bytes, in standard base64.","example":"RFNTRXYxIDI4IGFwcGxpY2F0aW9uL3ZuZC5pbi10b3RvK2pzb24gNDMgeyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEifQ==","type":"string"},"payload":{"description":"The exact in-toto statement bytes, in standard base64.","example":"eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEifQ==","type":"string"},"payloadType":{"description":"DSSE payload type: application/vnd.in-toto+json.","example":"application/vnd.in-toto+json","type":"string"},"purpose":{"description":"Signature purpose. Send test-acceptance.","example":"test-acceptance","type":"string"},"version":{"description":"Protected signer protocol version. Send 1.","example":1,"format":"int32","minimum":0,"type":"integer"}},"required":["version","purpose","by","keyid","payloadType","payload","pae"],"type":"object"},"SignInAgainRequest":{"description":"A person signed in anywhere asks for a link to sign in again, at their own address.","properties":{"next":{"description":"The page to open after signing in again.","type":"string"}},"type":"object"},"SignupDataSharing":{"description":"The optional choice shown before confirming a new Free account.","properties":{"wording":{"description":"The exact consent text to show before asking the person to choose.","example":"Help us improve Commitscale by sharing data from your checks, with secrets removed.","type":"string"},"wording_version":{"description":"Version of the consent text the person was shown.","example":"free-excerpts-2","type":"string"}},"required":["wording","wording_version"],"type":"object"},"SignupKeyIssued":{"allOf":[{"$ref":"#/components/schemas/CheckKeyIssued"},{"properties":{"billing_url":{"description":"A recoverable checkout failure leaves the new account on Free.","type":["string","null"]},"checkout_url":{"description":"Pro checkout after email confirmation. Only trusted billing delivery grants Pro.","type":["string","null"]}},"type":"object"}]},"SignupMode":{"description":"Whether creating an account is open or requires an invitation.","enum":["closed","open"],"type":"string"},"StartTestCheck":{"additionalProperties":false,"description":"Register one check against a workspace. Exact retries retain the same body.","properties":{"check_id":{"description":"Random UUID, reused for retries and focused reruns of the same plan.","example":"019a1f40-7c00-7000-8000-000000000001","format":"uuid","type":"string"},"cli_version":{"description":"Version of the calling CLI, such as 0.1.0.","example":"0.1.0","type":"string"},"protocol_version":{"description":"Wire protocol version. Send v1.","example":"v1","type":"string"},"workspace":{"description":"Workspace containing the repository. Omit for your personal workspace.","type":"string"}},"required":["check_id","protocol_version","cli_version"],"type":"object"},"SupportReceived":{"description":"The request is stored. A person replies to this account address.","properties":{"id":{"description":"Identifier of the stored support request.","format":"int64","type":"integer"},"reply_to":{"description":"The person's own address, or the agent's or check-only key's owner's.","example":"alex@example.com","type":"string"}},"required":["id","reply_to"],"type":"object"},"SupportRequest":{"additionalProperties":false,"properties":{"cli_version":{"description":"The CLI's version, in at most 100 characters.","example":"0.1.0","maxLength":100,"type":"string"},"message":{"description":"The message, trimmed, in 1 to 5,000 characters. Line breaks are kept.","example":"My test check cannot finish. How can I retry it?","maxLength":5000,"minLength":1,"type":"string"},"system":{"description":"The operating system and architecture, in at most 100 characters.","maxLength":100,"type":"string"}},"required":["message"],"type":"object"},"Team":{"properties":{"created_at":{"description":"When this record was created, in UTC.","format":"date-time","type":"string"},"full_name":{"description":"`acme/platform`.","example":"acme/platform","type":"string"},"members":{"description":"The people on it, such as `user:bob`.","items":{"type":"string"},"type":"array"},"name":{"description":"Short team name within its organization.","example":"platform","type":"string"},"org":{"description":"The organization's namespace name, such as acme.","example":"acme","type":"string"}},"required":["full_name","org","name","members","created_at"],"type":"object"},"TestAcceptanceKey":{"properties":{"keyid":{"description":"Identifier of the verification key. Pin it with the server and person you trust.","example":"acceptance-2026-10","type":"string"},"public_key":{"description":"Ed25519 public key in standard base64. Publication alone grants no trust.","example":"11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo=","type":"string"},"server":{"description":"Public URL of the server issuing the signature, without a trailing slash.","example":"https://commitscale.com","type":"string"}},"required":["server","keyid","public_key"],"type":"object"},"TestAcceptancePolicy":{"description":"Effective rule for the person behind this sign-in or check credential. This read grants no account-management powers.","properties":{"passkey_required":{"description":"Whether this person requires passkey-backed acceptance of findings.","type":"boolean"},"person":{"description":"Handle of the person responsible for this credential.","example":"user:alex","type":"string"}},"required":["person","passkey_required"],"type":"object"},"TestAcceptanceSigning":{"properties":{"confirmation":{"$ref":"#/components/schemas/Confirmation","description":"The pending or completed passkey confirmation for these exact statement bytes."},"envelope":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/Envelope","description":"The signed envelope after the owner's passkey confirms these exact bytes; null while pending."}]}},"required":["confirmation","envelope"],"type":"object"},"TestCheckStarted":{"properties":{"check_id":{"description":"Random UUID for this check, reused for exact retries.","example":"019a1f40-7c00-7000-8000-000000000001","format":"uuid","type":"string"},"protocol_version":{"description":"Wire protocol version. Send v1.","example":"v1","type":"string"}},"required":["check_id","protocol_version"],"type":"object"},"TestModelAnswer":{"enum":["Yes","No"],"type":"string"},"TestModelInput":{"description":"Scrubbed code or test evidence for the selected model question.","example":{"hunk":"@@ -1 +1 @@\n-return total;\n+return total + tax;\n","kind":"hunk","path":"src/total.rs"},"oneOf":[{"properties":{"hunk":{"description":"Changed lines as a unified diff hunk, with sensitive values removed.","example":"@@ -1 +1 @@\n-return total;\n+return total + tax;\n","type":"string"},"kind":{"description":"Selects the hunk shape.","enum":["hunk"],"type":"string"},"path":{"description":"Repository-relative file path. Do not send absolute machine paths.","example":"src/total.rs","type":"string"}},"required":["path","hunk","kind"],"type":"object"},{"properties":{"code":{"description":"Relevant source code around the proposed mistake, with sensitive values removed.","type":"string"},"edit":{"description":"The proposed edit, with sensitive values removed.","type":"string"},"kind":{"description":"Selects the mistake shape.","enum":["mistake"],"type":"string"},"path":{"description":"Repository-relative file path. Do not send absolute machine paths.","example":"src/total.rs","type":"string"}},"required":["path","code","edit","kind"],"type":"object"},{"properties":{"hunk":{"description":"One @@ hunk header and its body, without diff file headers; sensitive values removed.","example":"@@ -1 +1 @@\n-return total;\n+return total + tax;\n","type":"string"},"kind":{"description":"Selects the test_code shape.","enum":["test_code"],"type":"string"},"path":{"description":"Send (path hidden). This question must not use the file name as evidence.","example":"(path hidden)","type":"string"}},"required":["path","hunk","kind"],"type":"object"},{"properties":{"code_diff":{"description":"Source change as a unified diff, with sensitive values removed.","type":"string"},"edit":{"description":"The proposed edit, with sensitive values removed.","type":"string"},"kind":{"description":"Selects the test_edit shape.","enum":["test_edit"],"type":"string"}},"required":["code_diff","edit","kind"],"type":"object"},{"properties":{"kind":{"description":"Selects the build shape.","enum":["build"],"type":"string"},"log":{"description":"The end of the failed test run's log, with sensitive values removed.","type":"string"}},"required":["log","kind"],"type":"object"},{"properties":{"code_diff":{"description":"Source change as a unified diff, with sensitive values removed.","type":"string"},"edit":{"description":"The proposed edit, with sensitive values removed.","type":"string"},"kind":{"description":"Selects the caught shape.","enum":["caught"],"type":"string"},"test_diff":{"description":"Test change as a unified diff, or an empty string if no tests changed.","type":"string"}},"required":["code_diff","test_diff","edit","kind"],"type":"object"}]},"TestModelQuestion":{"enum":["behaviour","test_code","test_edit","build","caught"],"type":"string"},"TestQuestionAnswered":{"properties":{"answer":{"$ref":"#/components/schemas/TestModelAnswer","description":"Yes at p_yes >= 0.5. The CLI applies each question's policy separately."},"candidate_id":{"description":"Opaque candidate identifier. Do not put a path or source excerpt here.","example":"candidate:1","type":"string"},"check_id":{"description":"Random UUID for this check, reused for exact retries.","example":"019a1f40-7c00-7000-8000-000000000001","format":"uuid","type":"string"},"checkpoint":{"description":"Identifies the model that answered: its version, a colon, and a SHA-256 checksum.","example":"v17-f16:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef","pattern":"^[^:]+:[0-9a-f]{64}$","type":"string"},"model_version":{"description":"The served model's name, such as `v17-f16`.","example":"v17-f16","type":"string"},"p_yes":{"description":"Yes and No normalized against each other; not a policy threshold.","example":0.98,"format":"double","maximum":1,"minimum":0,"type":"number"},"prompt_sha256":{"description":"SHA-256 of the scrubbed, rendered chat input, as UTF-8 bytes.","example":"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef","pattern":"^[0-9a-f]{64}$","type":"string"},"prompt_version":{"description":"Prompt layout version. Send v17-layout-1.","example":"v17-layout-1","type":"string"},"question":{"$ref":"#/components/schemas/TestModelQuestion","description":"One of the five questions: behaviour, test_code, test_edit, build or caught."},"request_id":{"description":"Random UUID for this question, reused for exact retries.","example":"019a1f40-7c00-7000-8000-000000000002","format":"uuid","type":"string"},"template_version":{"description":"Chat template used to render this question, currently cscale-1-chat-v1.","example":"cscale-1-chat-v1","type":"string"},"tokenizer_version":{"description":"SHA-256 of the pinned tokenizer.json.","example":"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef","pattern":"^[0-9a-f]{64}$","type":"string"}},"required":["check_id","request_id","candidate_id","question","p_yes","answer","model_version","checkpoint","prompt_version","template_version","tokenizer_version","prompt_sha256"],"type":"object"},"Ticket":{"description":"What a one-time link does, before it is used.","properties":{"account":{"description":"The account it signs in to or links to; none for a signup.","type":"string"},"data_sharing":{"$ref":"#/components/schemas/SignupDataSharing","description":"The current optional sharing choice, only for a new Free account."},"email":{"description":"The address a signup creates an account for, or the provider's address for a link.","example":"person@example.com","type":"string"},"next":{"description":"Local page to open after redeeming this ticket.","type":"string"},"provider":{"$ref":"#/components/schemas/LoginProvider","description":"The provider whose identity a signup or a link brings along."},"purpose":{"$ref":"#/components/schemas/TicketPurpose","description":"Whether this ticket signs in, creates an account or links an identity."}},"required":["purpose"],"type":"object"},"TicketPurpose":{"enum":["sign_in","sign_up","link"],"type":"string"},"TicketRequest":{"description":"A one-time link's token, from `/login/continue#token=…`. A signup also names the account to create.","properties":{"data_sharing":{"$ref":"#/components/schemas/SetDataSharing","description":"Only for account creation, from the person's confirmation form."},"name":{"description":"Public username to create when accepting a sign-up ticket.","type":"string"},"token":{"description":"One-time secret from the sign-in or invitation link's fragment.","example":"email-fragment-token","type":"string"}},"required":["token"],"type":"object"},"TokenResponse":{"properties":{"actor":{"$ref":"#/components/schemas/Actor","description":"The identity authenticated by this token."},"token":{"description":"Personal access token, shown once. Keep it in the client's credential store.","example":"cs_pat_example","type":"string"},"token_display":{"description":"Safe display prefix for identifying the token without its secret.","example":"cs_pat_3f9a…","type":"string"}},"required":["token","token_display","actor"],"type":"object"},"UsageDay":{"description":"One UTC day of a namespace's use. Counts from the current day grow until it ends.","properties":{"api_out_bytes":{"description":"Bytes sent in API responses for this UTC day.","format":"int64","type":"integer"},"api_requests":{"description":"API requests attributed to this key or namespace, including failed requests.","format":"int64","type":"integer"},"day":{"description":"UTC calendar date in YYYY-MM-DD form.","format":"date","type":"string"},"git_in_bytes":{"description":"Reserved traffic measurement.","format":"int64","type":"integer"},"git_ms":{"description":"Reserved traffic measurement.","format":"int64","type":"integer"},"git_out_bytes":{"description":"Reserved traffic measurement.","format":"int64","type":"integer"},"log_bytes":{"description":"Reserved storage measurement; null until measured.","format":"int64","type":["integer","null"]},"model_checks":{"description":"Checks counted against this workspace on this UTC day.","format":"int64","type":"integer"},"repo_bytes":{"description":"Reserved storage measurement; null until measured.","format":"int64","type":["integer","null"]},"session_bytes":{"description":"Reserved storage measurement; null until measured.","format":"int64","type":["integer","null"]}},"required":["day","git_in_bytes","git_out_bytes","git_ms","model_checks","api_requests","api_out_bytes","repo_bytes","log_bytes","session_bytes"],"type":"object"},"UsageList":{"properties":{"days":{"description":"The latest day first. A day with no use has no entry.","items":{"$ref":"#/components/schemas/UsageDay"},"type":"array"},"model_checks":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/ModelCheckUsage","description":"This month's allowance for the personal workspace. Free counts checks in Free workspaces across verified accounts sharing a mailbox. Null for organizations."}]},"namespace":{"description":"The personal or organization namespace name.","example":"alex","type":"string"},"workspaces":{"description":"Your checks this month in each workspace you belong to. Omitted for other namespaces.","items":{"$ref":"#/components/schemas/WorkspaceCheckUsage"},"type":"array"}},"required":["namespace","model_checks","days"],"type":"object"},"WebLink":{"description":"A one-time sign-in link for a browser.","properties":{"expires_in_s":{"description":"Seconds until this request expires. Start again after it expires.","example":900,"format":"int64","minimum":0,"type":"integer"},"url":{"description":"Absolute URL to open for the next step.","example":"https://commitscale.com/login/web#code=web-sign-in-code","type":"string"}},"required":["url","expires_in_s"],"type":"object"},"WebLinkInfo":{"description":"What a `cscale web` link does, before it is used: the account it signs the browser in to.","properties":{"account":{"description":"The account's username, such as `john`.","example":"alex","type":"string"}},"required":["account"],"type":"object"},"WebLinkRequest":{"properties":{"next":{"description":"The page to open after signing in, such as `/acme/app`.","type":"string"}},"type":"object"},"WebSessionRequest":{"properties":{"code":{"description":"One-time code from a web sign-in link. Redeeming it sets the session cookie.","example":"web-sign-in-code","type":"string"}},"required":["code"],"type":"object"},"WorkspaceCheckUsage":{"properties":{"checks":{"description":"Your checks started in this workspace during the current UTC month.","format":"int64","type":"integer"},"namespace":{"description":"Workspace namespace name.","type":"string"},"plan":{"description":"The workspace's effective plan.","type":"string"}},"required":["namespace","plan","checks"],"type":"object"}},"securitySchemes":{"session":{"description":"The web app's session, `cs_ses_…`, set when a browser signs in; named `cs_session` on a server without HTTPS. It counts only for `/api/`, and for requests that change anything only when their `Origin` is this server. It is refused as a bearer token.","in":"cookie","name":"__Host-cs_session","type":"apiKey"},"token":{"description":"A bearer credential: cs_chk_ for a check key, cs_pat_ for a person, or cs_agt_ for an agent. A check key can call only operations marked x-commitscale-check-key. OAuth access tokens work only at /mcp.","scheme":"bearer","type":"http"}}},"info":{"description":"Operations included by Free or Pro on this server. Each operation names its feature flag. Authentication and permission requirements still apply. See /docs/api for the guide.","title":"Commitscale API","version":"1"},"openapi":"3.1.0","paths":{"/.well-known/oauth-authorization-server":{"get":{"description":"Returns the issuer, OAuth endpoints, supported grants, scopes and PKCE method for connecting to this server's MCP resource.","operationId":"oauthAuthorizationServerMetadata","responses":{"200":{"content":{"application/json":{"example":{"authorization_endpoint":"example","authorization_response_iss_parameter_supported":false,"client_id_metadata_document_supported":false,"code_challenge_methods_supported":["example"],"grant_types_supported":["example"],"issuer":"example","registration_endpoint":"example","response_modes_supported":["example"],"response_types_supported":["example"],"revocation_endpoint":"example","revocation_endpoint_auth_methods_supported":["example"],"scopes_supported":["example"],"token_endpoint":"example","token_endpoint_auth_methods_supported":["example"]},"schema":{"$ref":"#/components/schemas/AuthorizationServerMetadata"}}},"description":"OAuth authorization server metadata.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"security":[{}],"summary":"Discover the authorization server","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"remote_mcp","x-commitscale-problems":[{"code":"quota_exceeded","statuses":[429]}],"x-commitscale-public":true}},"/.well-known/oauth-protected-resource/mcp":{"get":{"description":"Returns the remote MCP resource, its authorization server, supported scopes and bearer authentication method.","operationId":"oauthMcpResourceMetadata","responses":{"200":{"content":{"application/json":{"example":{"authorization_servers":["example"],"bearer_methods_supported":["example"],"resource":"example","resource_name":"example","scopes_supported":["example"]},"schema":{"$ref":"#/components/schemas/ResourceMetadata"}}},"description":"MCP protected resource metadata.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"security":[{}],"summary":"Discover the MCP resource","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"remote_mcp","x-commitscale-problems":[{"code":"quota_exceeded","statuses":[429]}],"x-commitscale-public":true}},"/api/v1/account":{"get":{"description":"Reads the signed-in person's public username and verified sign-in address. Delegated credentials cannot read this profile.","operationId":"getAccount","responses":{"200":{"content":{"application/json":{"example":{"email":"person@example.com","name":"alex"},"schema":{"$ref":"#/components/schemas/Account"}}},"description":"Your account.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"summary":"Your account's name and sign-in email","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true}},"/api/v1/account/data-sharing":{"delete":{"description":"Turns off data sharing and deletes retained excerpts. For the person or any credential acting for them. Takes no body or wording version. Turns consent off, deletes all retained excerpts immediately, and records a withdrawal event in one transaction. An in-flight answer or replay cannot restore them. Repeating the call repeats the deletion safely. A check-only key can call it.","operationId":"withdrawDataSharing","responses":{"200":{"content":{"application/json":{"example":{"current_wording_version":"free-excerpts-2","opted_in":false,"opted_in_at":"2026-10-26T10:00:00Z","plan":"free","withdrawn_at":"2026-10-26T10:00:00Z","wording":"Help us improve Commitscale by sharing data from your checks, with secrets removed.","wording_version":"free-excerpts-2"},"schema":{"$ref":"#/components/schemas/DataSharing"}}},"description":"Consent is off and retained excerpts are deleted.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"summary":"Withdraw data sharing and delete excerpts","tags":["auth"],"x-commitscale-check-key":true,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true},"get":{"description":"Returns your data-sharing choice and its current wording. Sharing check data to help improve Commitscale is optional and off by default. Returns the responsible person's own plan and consent, including the accepted wording version and current wording. Earlier consents keep their accepted version until the person explicitly accepts the current one. A personal token, browser session, phone token, or regular agent, runner or job token can read the responsible person's choice. A check-only key cannot.","operationId":"getDataSharing","responses":{"200":{"content":{"application/json":{"example":{"current_wording_version":"free-excerpts-2","opted_in":false,"opted_in_at":"2026-10-26T10:00:00Z","plan":"free","withdrawn_at":"2026-10-26T10:00:00Z","wording":"Help us improve Commitscale by sharing data from your checks, with secrets removed.","wording_version":"free-excerpts-2"},"schema":{"$ref":"#/components/schemas/DataSharing"}}},"description":"The person's consent.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"summary":"Read your data-sharing choice","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true},"put":{"description":"Updates your choice to share Free-plan check data. Only a person's own personal token or browser session can opt in, on Free, with the current wording_version. Agents and check-only keys must never opt in. opted_in=false withdraws without requiring a version and deletes every retained excerpt in the same transaction. No idempotency key is needed.","operationId":"setDataSharing","requestBody":{"content":{"application/json":{"example":{"opted_in":false,"wording_version":"free-excerpts-2"},"schema":{"$ref":"#/components/schemas/SetDataSharing"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"current_wording_version":"free-excerpts-2","opted_in":false,"opted_in_at":"2026-10-26T10:00:00Z","plan":"free","withdrawn_at":"2026-10-26T10:00:00Z","wording":"Help us improve Commitscale by sharing data from your checks, with secrets removed.","wording_version":"free-excerpts-2"},"schema":{"$ref":"#/components/schemas/DataSharing"}}},"description":"The saved choice.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"Only the person on Free may opt in."},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest","description":"The consent wording version is missing or out of date."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"summary":"Choose whether to share check data","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"quota_exceeded","statuses":[422,429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true}},"/api/v1/account/passkey-layer":{"get":{"description":"Reads your rule for protecting check-key creation and acceptance of test findings with a passkey. People only. The layer starts off.","operationId":"getPasskeyLayer","responses":{"200":{"content":{"application/json":{"example":{"enabled":false},"schema":{"$ref":"#/components/schemas/PasskeyLayer"}}},"description":"Your setting. Off by default.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"People only."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"summary":"Read your optional passkey layer","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true},"put":{"description":"Changes your optional passkey layer. People only. Turning on needs a registered passkey. Turning off returns a confirmation; only the person's passkey applies it. Repeating an unchanged setting has no event.","operationId":"setPasskeyLayer","requestBody":{"content":{"application/json":{"example":{"enabled":false},"schema":{"$ref":"#/components/schemas/SetPasskeyLayer"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"enabled":false},"schema":{"$ref":"#/components/schemas/PasskeyLayer"}}},"description":"The setting, with a pending confirmation when turning off.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"People only, with a registered passkey to turn on."},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"summary":"Change your optional passkey layer","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"already_exists","statuses":[409]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true}},"/api/v1/auth/device":{"post":{"description":"The OAuth 2.0 device authorization grant, as RFC 8628 describes it, which `cscale auth login` uses. With an email, the approval link goes there, and the person types the terminal's user code on its page. Five wrong codes refuse the login. The answer is the same whether or not an account uses the address: it comes before the server looks, and the email follows. Without an email, the person opens `verification_uri`, signs in, and types the user code. Then poll for the token every `interval_s` seconds. Needs no login; limited per client address. When email limits are enabled, an address gets at most 5 an hour and 10 a day, and each place that asks, and the server, send only so much email that nobody uses.","operationId":"startDeviceLogin","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"example":{"client_name":"cscale","email":"person@example.com"},"schema":{"$ref":"#/components/schemas/DeviceStartRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"device_code":"device-polling-code","expires_in_s":900,"interval_s":5,"message":"Open the verification link and enter your code.","user_code":"BCDF-GHJK"},"schema":{"$ref":"#/components/schemas/DeviceStartResponse"}}},"description":"The device code to poll with, and the code to show.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest","description":"The email address is not valid."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"security":[{}],"summary":"Start a person's login","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"signup_closed","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"quota_exceeded","statuses":[422,429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/auth/device/code":{"post":{"description":"Approves or denies a terminal login. For the web app's device page, from a browser that a person signed in to with an email link, a passkey, or a provider, within the last 10 minutes; a token cannot, so it cannot mint another that outlives it. An older session answers 403 with `next.action` `sign_in`: sign in again, then send the code again; the web app keeps it through that sign-in. Without `approve`, looks up the login waiting for the code a terminal shows; with it, answers. `client_name` is what the terminal calls itself, its own words; `requested_from` and `requested_at` say where and when it asked.","operationId":"answerDeviceCode","parameters":[{"description":"This server's origin. Required with the browser session cookie.","example":"https://commitscale.com","in":"header","name":"Origin","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"example":{"user_code":"BCDF-GHJK"},"schema":{"$ref":"#/components/schemas/DeviceCodeRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"client_name":"cscale","expires_at":"2026-10-26T10:00:00Z","requested_at":"2026-10-26T10:00:00Z","status":"pending","user_code":"BCDF-GHJK"},"schema":{"$ref":"#/components/schemas/DeviceLogin"}}},"description":"The login, and its status now.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The caller is not a browser that a person signed in to in the last 10 minutes."},"404":{"$ref":"#/components/responses/NotFound","description":"No login waits for that code."},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest","description":"The code is not eight letters."},"429":{"$ref":"#/components/responses/TooManyRequests","description":"Too many codes tried this minute."},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"security":[{"session":[]}],"summary":"Approve or deny a terminal's login","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"quota_exceeded","statuses":[422,429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"expired_token","statuses":[400]},{"code":"access_denied","statuses":[400]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/auth/device/token":{"post":{"description":"Reports a terminal login's status and returns its credential when approved. Answers `authorization_pending` until the person approves, `slow_down` when polled more often than every 5 seconds, `access_denied` when they deny or five wrong codes refuse the emailed approval, and `expired_token` after 15 minutes or once used. Once approved, the next poll gets a personal token, shown once. Needs no login.","operationId":"pollDeviceLogin","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"example":{"device_code":"example-device-code"},"schema":{"$ref":"#/components/schemas/DeviceTokenRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"actor":{"handle":"user:alex","is_admin":false,"kind":"user","name":"alex"},"token":"cs_pat_example","token_display":"cs_pat_3f9a…"},"schema":{"$ref":"#/components/schemas/TokenResponse"}}},"description":"The token, and who it is for.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest","description":"Not approved yet, polled too fast, denied, or expired: see `code`."},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"security":[{}],"summary":"Poll for a person's token","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"quota_exceeded","statuses":[429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"authorization_pending","statuses":[400]},{"code":"slow_down","statuses":[400]},{"code":"expired_token","statuses":[400]},{"code":"access_denied","statuses":[400]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/auth/email-link":{"post":{"description":"Emails a link to sign in or sign up. For the web app's sign-in page; a page on another site is refused. Emails a link that signs in, or, when signup is open to the address, one that signs up. The answer is the same either way, and when nothing is sent, so it never says who has an account. Limited per client address. When email limits are enabled, an address gets at most 5 an hour and 10 a day; limits also apply to each network and the server.","operationId":"requestEmailLink","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"example":{"email":"person@example.com"},"schema":{"$ref":"#/components/schemas/EmailLinkRequest"}}},"required":true},"responses":{"202":{"content":{"application/json":{"example":{"expires_in_s":900,"message":"Check your email to finish sign-in."},"schema":{"$ref":"#/components/schemas/EmailLinkSent"}}},"description":"A link is on its way, if the address may have one.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The request came from another site's page."},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest","description":"The email address is not valid."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"security":[{}],"summary":"Email a sign-in link","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"signup_closed","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"quota_exceeded","statuses":[422,429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/auth/identities":{"get":{"description":"Lists your linked accounts at sign-in providers. People only: their accounts at sign-in providers.","operationId":"listIdentities","responses":{"200":{"content":{"application/json":{"example":{"identities":[{"created_at":"2026-10-26T10:00:00Z","email":"person@example.com","id":1,"provider":{"id":"google","name":"Google"}}]},"schema":{"$ref":"#/components/schemas/IdentityList"}}},"description":"The linked accounts.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The caller is not a person."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"summary":"The caller's linked accounts","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true}},"/api/v1/auth/identities/{id}":{"delete":{"description":"Unlinks an account at a sign-in provider. People only: one of their linked accounts.","operationId":"unlinkIdentity","parameters":[{"description":"The linked account's `id`.","example":1,"in":"path","name":"id","required":true,"schema":{"format":"int64","type":"integer"}}],"responses":{"204":{"description":"Unlinked.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The caller is not a person."},"404":{"$ref":"#/components/responses/NotFound","description":"The caller has no linked account with that ID."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"summary":"Unlink an account at a provider","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"invalid_request","statuses":[400]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true}},"/api/v1/auth/invites/accept":{"post":{"description":"Accepts an invitation from its emailed link. For the web app's own pages only. For an address without an account, creates it under `name`, even while signup is closed, and signs the browser in. For an address with one, only a browser already signed in to that account accepts: the link alone never signs in. Then it joins the invite's organization, if it names one. An organization owner's invites create at most five accounts a day, only at the domains signup takes, if it names any, and none if the owner's own account came from an owner's invite.","operationId":"acceptInvite","parameters":[{"description":"This server's origin. Required for this browser sign-in step.","example":"https://commitscale.com","in":"header","name":"Origin","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"example":{"token":"email-fragment-token"},"schema":{"$ref":"#/components/schemas/TicketRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"handle":"user:alex","is_admin":false,"kind":"user","name":"alex"},"schema":{"$ref":"#/components/schemas/Actor"}}},"description":"Accepted: the person, signed in.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest","description":"The link expired, was used, or was withdrawn."},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The browser cannot accept the invite, the server takes no new account at the address, or the organization's Free plan cannot add another person."},"409":{"$ref":"#/components/responses/Conflict","description":"The username is taken."},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest","description":"No username for a new account, or one that is not valid."},"429":{"$ref":"#/components/responses/TooManyRequests","description":"The server made all the accounts it makes from owners' invites today."},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"security":[{}],"summary":"Accept an invite from an email link","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"organization_person_limit","statuses":[403]},{"code":"check_key_scope","statuses":[403]},{"code":"signup_closed","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"already_exists","statuses":[409]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"quota_exceeded","statuses":[422,429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"expired_token","statuses":[400]},{"code":"access_denied","statuses":[400]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/auth/invites/check":{"post":{"description":"Shows what an invitation link does before it is used. For the web app's own pages only. Says, before the link from `/join#token=…` is used, who sent the invite, what it joins, and the account that uses its address, if one does, which must be signed in to accept.","operationId":"checkInvite","parameters":[{"description":"This server's origin. Required for this browser sign-in step.","example":"https://commitscale.com","in":"header","name":"Origin","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"example":{"token":"email-fragment-token"},"schema":{"$ref":"#/components/schemas/TicketRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"email":"person@example.com","expires_at":"2026-10-26T10:00:00Z","invited_by":"alex","org":"acme"},"schema":{"$ref":"#/components/schemas/InviteTicket"}}},"description":"What the link does.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest","description":"The link expired, was used, or was withdrawn."},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The request did not come from this server's own page."},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"security":[{}],"summary":"What an invite's link does","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"signup_closed","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"already_exists","statuses":[409]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"quota_exceeded","statuses":[429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"expired_token","statuses":[400]},{"code":"access_denied","statuses":[400]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/auth/logout":{"post":{"description":"Revokes the token this request carries. A browser also drops its session cookie.","operationId":"logout","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"204":{"description":"Revoked.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"summary":"Revoke the token in use","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"invalid_request","statuses":[400]},{"code":"quota_exceeded","statuses":[429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/auth/methods":{"get":{"description":"Lists the server's enabled sign-in methods and signup mode. The ways to sign in that this server has turned on: email links, passkeys, and OpenID Connect providers, and whether anyone may sign up. Anyone may read it.","operationId":"getLoginMethods","responses":{"200":{"content":{"application/json":{"example":{"email_link":false,"passkeys":false,"providers":[{"id":"google","name":"Google"}],"signup":"closed"},"schema":{"$ref":"#/components/schemas/LoginMethods"}}},"description":"The ways to sign in.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"security":[{}],"summary":"How people can sign in here","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true}},"/api/v1/auth/passkey/challenge":{"post":{"description":"Starts signing in with a passkey. For the web app's sign-in page. A page on another site is refused. Returns the options for navigator.credentials.get; the browser offers the passkeys it has for this site. Finish with POST /api/v1/auth/passkey/session. Limited per address.","operationId":"startPasskeySignIn","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"content":{"application/json":{"example":{"ceremony":"019a1f40-7c00-7000-8000-000000000005","options":{}},"schema":{"$ref":"#/components/schemas/PasskeyChallenge"}}},"description":"The ceremony.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The request came from another site's page."},"404":{"$ref":"#/components/responses/NotFound","description":"Passkeys are off on this server."},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"security":[{}],"summary":"Start signing in with a passkey","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"already_exists","statuses":[409]},{"code":"invalid_request","statuses":[400]},{"code":"quota_exceeded","statuses":[429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"expired_token","statuses":[400]},{"code":"access_denied","statuses":[400]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/auth/passkey/session":{"post":{"description":"Signs a browser in with a passkey. For the web app's own sign-in page only. Checks the passkey's answer and signs the browser in as the account it names, with the session cookie.","operationId":"finishPasskeySignIn","parameters":[{"description":"This server's origin. Required for this browser sign-in step.","example":"https://commitscale.com","in":"header","name":"Origin","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"example":{"ceremony":"019a1f40-7c00-7000-8000-000000000005","credential":{}},"schema":{"$ref":"#/components/schemas/PasskeyFinishRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"handle":"user:alex","is_admin":false,"kind":"user","name":"alex"},"schema":{"$ref":"#/components/schemas/Actor"}}},"description":"Signed in; the session cookie is set.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest","description":"The prompt expired, or the passkey does not sign in here."},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The request did not come from this server's own page."},"404":{"$ref":"#/components/responses/NotFound","description":"Passkeys are off on this server."},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest","description":"The credential is not valid."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"security":[{}],"summary":"Sign in with a passkey","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"already_exists","statuses":[409]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"quota_exceeded","statuses":[422,429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"expired_token","statuses":[400]},{"code":"access_denied","statuses":[400]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/auth/passkeys":{"get":{"description":"Lists your passkeys. People only.","operationId":"listPasskeys","responses":{"200":{"content":{"application/json":{"example":{"passkeys":[{"created_at":"2026-10-26T10:00:00Z","id":1,"name":"Work laptop"}]},"schema":{"$ref":"#/components/schemas/PasskeyList"}}},"description":"The passkeys.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The caller is not a person."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"summary":"The caller's passkeys","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true},"post":{"description":"Starts adding a passkey to your account. From a browser that a person signed in to with an email link, a passkey, or a provider, within the last 10 minutes; a token cannot, since a passkey outlives any token. An older session answers 403 with `next.action` `sign_in`: sign in again, then start again. With the passkey layer on, first confirm with an existing passkey at the returned `next` URL, then retry. Returns the options for `navigator.credentials.create`, and the ceremony that finishes it.","operationId":"startPasskeyRegistration","parameters":[{"description":"This server's origin. Required with the browser session cookie.","example":"https://commitscale.com","in":"header","name":"Origin","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"example":{},"schema":{"$ref":"#/components/schemas/PasskeyRegisterRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"ceremony":"019a1f40-7c00-7000-8000-000000000005","options":{}},"schema":{"$ref":"#/components/schemas/PasskeyChallenge"}}},"description":"The ceremony.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The caller is not a browser that a person signed in to in the last 10 minutes."},"404":{"$ref":"#/components/responses/NotFound","description":"Passkeys are off on this server."},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest","description":"The name is too long, or the person has as many passkeys as one may."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"security":[{"session":[]}],"summary":"Start adding a passkey","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"already_exists","statuses":[409]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"quota_exceeded","statuses":[422,429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"expired_token","statuses":[400]},{"code":"access_denied","statuses":[400]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/auth/passkeys/finish":{"post":{"description":"From the same signed-in browser: checks the new credential and keeps it. Without a name from the start, it takes the name given here, or `Passkey`.","operationId":"finishPasskeyRegistration","parameters":[{"description":"This server's origin. Required with the browser session cookie.","example":"https://commitscale.com","in":"header","name":"Origin","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"example":{"ceremony":"019a1f40-7c00-7000-8000-000000000005","credential":{}},"schema":{"$ref":"#/components/schemas/PasskeyAddRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"example":{"created_at":"2026-10-26T10:00:00Z","id":1,"name":"Work laptop"},"schema":{"$ref":"#/components/schemas/PasskeyInfo"}}},"description":"The passkey.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest","description":"The prompt expired or was already answered."},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The caller is not a browser that a person signed in to."},"404":{"$ref":"#/components/responses/NotFound","description":"Passkeys are off on this server."},"409":{"$ref":"#/components/responses/Conflict","description":"That passkey is registered already."},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest","description":"The credential is not valid, or could not be checked."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"security":[{"session":[]}],"summary":"Finish adding a passkey","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"already_exists","statuses":[409]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"quota_exceeded","statuses":[422,429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"expired_token","statuses":[400]},{"code":"access_denied","statuses":[400]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/auth/passkeys/{id}":{"delete":{"description":"Removes one of your passkeys. People only: one of their passkeys.","operationId":"removePasskey","parameters":[{"description":"The passkey's `id`.","example":1,"in":"path","name":"id","required":true,"schema":{"format":"int64","type":"integer"}}],"responses":{"204":{"description":"Removed.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The caller is not a person."},"404":{"$ref":"#/components/responses/NotFound","description":"The caller has no passkey with that ID."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"summary":"Remove a passkey","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"invalid_request","statuses":[400]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true}},"/api/v1/auth/providers/{provider}/link":{"post":{"description":"Starts linking an account at a sign-in provider. From a browser that a person signed in to with an email link, a passkey, or a provider, within the last 10 minutes; a token cannot, since a linked account outlives any token. An older session answers 403 with `next.action` `sign_in`: sign in again, then start again. Returns the provider's authorization page to send the browser to.","operationId":"startProviderLink","parameters":[{"description":"This server's origin. Required with the browser session cookie.","example":"https://commitscale.com","in":"header","name":"Origin","required":true,"schema":{"type":"string"}},{"description":"The provider, such as `google`.","example":"google","in":"path","name":"provider","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"content":{"application/json":{"example":{"url":"https://accounts.google.com/o/oauth2/v2/auth"},"schema":{"$ref":"#/components/schemas/ProviderRedirect"}}},"description":"Where to send the browser.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The caller is not a browser that a person signed in to in the last 10 minutes."},"404":{"$ref":"#/components/responses/NotFound","description":"No such provider on this server."},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"502":{"$ref":"#/components/responses/Unavailable","description":"The provider could not be reached."},"503":{"$ref":"#/components/responses/Busy"}},"security":[{"session":[]}],"summary":"Start linking an account at a provider","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"invalid_request","statuses":[400]},{"code":"quota_exceeded","statuses":[429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[502,503]}],"x-commitscale-public":true}},"/api/v1/auth/providers/{provider}/start":{"post":{"description":"Starts signing in with a provider. For the web app's own sign-in page only: the request's `Origin` must be this server. Returns the provider's authorization page to send the browser to; the provider sends it back to `/login/oidc/{provider}/callback`, which signs it in. Limited per address.","operationId":"startProviderSignIn","parameters":[{"description":"This server's origin. Required for this browser sign-in step.","example":"https://commitscale.com","in":"header","name":"Origin","required":true,"schema":{"type":"string"}},{"description":"The provider, as `/api/v1/auth/methods` lists it, such as `google`.","example":"google","in":"path","name":"provider","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"example":{},"schema":{"$ref":"#/components/schemas/ProviderStartRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"url":"https://accounts.google.com/o/oauth2/v2/auth"},"schema":{"$ref":"#/components/schemas/ProviderRedirect"}}},"description":"Where to send the browser.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The request did not come from this server's own page."},"404":{"$ref":"#/components/responses/NotFound","description":"No such provider on this server."},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"502":{"$ref":"#/components/responses/Unavailable","description":"The provider could not be reached."},"503":{"$ref":"#/components/responses/Busy"}},"security":[{}],"summary":"Start signing in with a provider","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"quota_exceeded","statuses":[429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[502,503]}],"x-commitscale-public":true}},"/api/v1/auth/sign-in-again":{"post":{"description":"Emails a link to sign in again. From a browser signed in any way, at any time, or with a phone's own token. Emails a sign-in link to the caller's own address, outside the caps on sign-in email, which someone else could fill. When email limits are enabled, this allows three a day for each session or phone, where a browser opened with `cscale web` counts as the CLI login or phone it came from, and ten a day for each person. The caller stays signed in until the link signs them in again; then the session that asked ends, and the new one replaces it. A sign-in with it is recent, so for 10 minutes it may add a passkey, link an account, or approve a terminal. With `app`, from a phone, the link opens `/login/app`, as `POST /api/v1/auth/email-link` with `app` does, for the app to sign in with or to use as fresh proof to add a passkey; the phone keeps its token.","operationId":"signInAgain","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"example":{},"schema":{"$ref":"#/components/schemas/SignInAgainRequest"}}},"required":true},"responses":{"202":{"content":{"application/json":{"example":{"expires_in_s":900,"message":"Check your email to finish sign-in."},"schema":{"$ref":"#/components/schemas/EmailLinkSent"}}},"description":"The link is on its way.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The caller is not a browser's session, or a phone's token."},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/TooManyRequests","description":"This session or phone, or this person, asked for as many today as it may."},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy","description":"The email could not be sent."}},"security":[{"session":[]},{"token":[]}],"summary":"Email yourself a link to sign in again","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"quota_exceeded","statuses":[429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/auth/signup":{"post":{"description":"Starts sign-up for a person and their agent. Available with CS_SIGNUP=open. Returns the same polling secret and message shape for new and existing addresses, before looking up an account. Only a new, allowed address gets a verification email. No account or credential is created before verification. Starts and verification emails have separate rolling-hour limits per address and IPv4 /24 or IPv6 /56, independent of CS_EMAIL_CAPS. Keep signup_code private and poll at interval_s. No verification secret is returned.","operationId":"startAgentSignup","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"example":{"email":"person@example.com","key_name":"My coding agent","plan":"free"},"schema":{"$ref":"#/components/schemas/AgentSignupRequest"}}},"required":true},"responses":{"202":{"content":{"application/json":{"example":{"expires_in_s":900,"interval_s":5,"message":"Check your email to confirm your account.","signup_code":"signup-polling-code"},"schema":{"$ref":"#/components/schemas/AgentSignupStarted"}}},"description":"Poll while the person checks their email.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"Sign-up is closed, or a different website sent the request."},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest","description":"Invalid email or key name."},"429":{"$ref":"#/components/responses/TooManyRequests","description":"The address or network reached its start limit."},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy","description":"Email delivery is not configured."}},"security":[{}],"summary":"Start sign-up for a person and their agent","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"signup_closed","statuses":[403]},{"code":"signup_limited","statuses":[429]},{"code":"feature_off","statuses":[403]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"quota_exceeded","statuses":[422,429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/auth/signup/check":{"post":{"description":"Previews an emailed sign-up without verifying it. The server's own page sends the email fragment secret. Returns the address, key name and optional sharing wording. Does not consume the link, create an account, authorize a key or sign in. The polling code cannot read this preview.","operationId":"checkAgentSignup","parameters":[{"description":"This server's origin. Required for this browser sign-in step.","example":"https://commitscale.com","in":"header","name":"Origin","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"example":{"token":"email-fragment-token"},"schema":{"$ref":"#/components/schemas/AgentSignupVerify"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"email":"person@example.com","key_name":"My coding agent","plan":"free"},"schema":{"$ref":"#/components/schemas/AgentSignupPreview"}}},"description":"The sign-up to confirm.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest","description":"The link expired, was used, or cannot create an account."},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"Sign-up is closed or the request came from another site."},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"security":[{}],"summary":"Preview an emailed sign-up without verifying it","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"signup_closed","statuses":[403]},{"code":"signup_limited","statuses":[429]},{"code":"feature_off","statuses":[403]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"quota_exceeded","statuses":[429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"expired_token","statuses":[400]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/auth/signup/key":{"post":{"description":"Collects an agent's check-only key after sign-up. Poll with signup_code every 5 seconds. authorization_pending means the email has not been verified, including for an existing address. slow_down carries Retry-After. expired_token means the request expired, its key was revoked, or a poll already collected it. One successful poll returns a cs_chk_ token; it is never stored or replayed. A lost successful response needs a new key issued by the person after signing in.","operationId":"pollAgentSignup","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"example":{"signup_code":"example-signup-code"},"schema":{"$ref":"#/components/schemas/AgentSignupPoll"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"key":{"agent":"agent:check-1@alex","created_at":"2026-10-26T10:00:00Z","expires_at":"2026-10-26T10:00:00Z","id":1,"last_used_at":"2026-10-26T10:00:00Z","name":"My coding agent","revoked_at":"2026-10-26T10:00:00Z"},"token":"cs_chk_example"},"schema":{"$ref":"#/components/schemas/SignupKeyIssued"}}},"description":"The check-only key, shown once.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest","description":"Pending, slow down, or expired: see code."},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"security":[{}],"summary":"Collect the agent's check-only key once","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"quota_exceeded","statuses":[429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"authorization_pending","statuses":[400]},{"code":"slow_down","statuses":[400]},{"code":"expired_token","statuses":[400]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/auth/signup/verify":{"post":{"description":"Verifies an email and creates the person's account. Only the person's Confirm press sends the email fragment secret and optional versioned sharing choice from this server's own origin. Works once within 15 minutes. Creates a new person, namespace and agent without repository grants, and signs this browser in for onboarding. Never signs in or issues credentials for an existing account, including one created since the email went out. Returns no key. The agent collects its check-only key separately by polling.","operationId":"verifyAgentSignup","parameters":[{"description":"This server's origin. Required for this browser sign-in step.","example":"https://commitscale.com","in":"header","name":"Origin","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"example":{"token":"email-fragment-token"},"schema":{"$ref":"#/components/schemas/AgentSignupVerify"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"account":"alex","key_name":"My coding agent"},"schema":{"$ref":"#/components/schemas/AgentSignupVerified"}}},"description":"The account is ready.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest","description":"The link expired, was used, or cannot create an account."},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"Sign-up closed or the request did not come from this server's page."},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"security":[{}],"summary":"Verify the email and create the person's account","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"signup_closed","statuses":[403]},{"code":"signup_limited","statuses":[429]},{"code":"feature_off","statuses":[403]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"quota_exceeded","statuses":[429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"expired_token","statuses":[400]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/auth/tickets/check":{"post":{"description":"Shows what a one-time link does before it is used. For the web app's own pages only. Says what a link from an email does, before it is used: sign in, sign up, or link an account at a provider.","operationId":"checkTicket","parameters":[{"description":"This server's origin. Required for this browser sign-in step.","example":"https://commitscale.com","in":"header","name":"Origin","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"example":{"token":"email-fragment-token"},"schema":{"$ref":"#/components/schemas/TicketRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"email":"person@example.com","purpose":"sign_in"},"schema":{"$ref":"#/components/schemas/Ticket"}}},"description":"What the link does.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest","description":"The link expired or was used."},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The request did not come from this server's own page."},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"security":[{}],"summary":"What a one-time link does","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"signup_closed","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"already_exists","statuses":[409]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"quota_exceeded","statuses":[429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"expired_token","statuses":[400]},{"code":"access_denied","statuses":[400]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/auth/tickets/redeem":{"post":{"description":"Uses a one-time link to sign in, link an account or sign up. For the web app's own pages only. Signs in, links the account at a provider that the link carries, or creates the account it is for, under `name`; each signs the browser in with the session cookie.","operationId":"redeemTicket","parameters":[{"description":"This server's origin. Required for this browser sign-in step.","example":"https://commitscale.com","in":"header","name":"Origin","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"example":{"token":"email-fragment-token"},"schema":{"$ref":"#/components/schemas/TicketRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"handle":"user:alex","is_admin":false,"kind":"user","name":"alex"},"schema":{"$ref":"#/components/schemas/Actor"}}},"description":"Signed in; the session cookie is set.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest","description":"The link expired or was used."},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The request did not come from this server's own page, or signup is closed to the address."},"409":{"$ref":"#/components/responses/Conflict","description":"The username is taken, or the account at the provider is linked to someone else."},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest","description":"No username for a signup, or one that is not valid."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"security":[{}],"summary":"Use a one-time link","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"signup_closed","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"already_exists","statuses":[409]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"quota_exceeded","statuses":[422,429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"expired_token","statuses":[400]},{"code":"access_denied","statuses":[400]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/auth/tokens":{"get":{"description":"Lists your active CLI logins and browser sessions. People only: their own CLI logins and browser sessions that still work, newest first, with when each was last used. current marks this request's sign-in, and revocable marks older sign-ins it can end.","operationId":"listLoginTokens","responses":{"200":{"content":{"application/json":{"example":{"tokens":[{"created_at":"2026-10-26T10:00:00Z","current":false,"expires_at":"2026-10-26T10:00:00Z","id":1,"kind":"cli","revocable":false,"token":"cs_pat_3f9a…"}]},"schema":{"$ref":"#/components/schemas/LoginTokenList"}}},"description":"The tokens.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"401":{"$ref":"#/components/responses/Unauthenticated","description":"Not signed in."},"403":{"$ref":"#/components/responses/Forbidden","description":"The caller is an agent or a runner."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"summary":"List your CLI logins and browser sessions","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true}},"/api/v1/auth/tokens/revoke-others":{"post":{"description":"Signs out your older CLI logins, browser sessions and phones, and ends connected apps approved before this sign-in. Signs out every CLI login, browser session, and phone of the caller's that is older than the sign-in this request came with, but not that one, nor the CLI login a `cscale web` session came from. For a token that may have leaked, when it is not clear which. Newer sign-ins stay, so a fresh sign-in always wins: sign in again to sign them out too. It needs a browser the caller signed in to with an email link, a passkey or a provider, or else their passkey on this request: a pending request returns a problem with `next.action` `confirm`; repeat it in the same client after confirming.","operationId":"revokeOtherLoginTokens","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"content":{"application/json":{"example":{"newer":0,"revoked":0},"schema":{"$ref":"#/components/schemas/LoginTokensRevoked"}}},"description":"How many it signed out, and how many newer ones it left.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated","description":"Not signed in."},"403":{"$ref":"#/components/responses/Forbidden","description":"The caller is an agent or a runner, or the request waits for the caller's passkey."},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"summary":"Sign out everywhere else","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"invalid_request","statuses":[400]},{"code":"quota_exceeded","statuses":[429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/auth/tokens/{id}":{"delete":{"description":"Revokes it at once: its next request is refused, and an event stream it has open ends. Revoking a CLI login also ends the browser sessions `cscale web` opened with it. The token this request came with revokes itself with nothing more, as logout does. Another needs a browser the caller signed in to with an email link, a passkey or a provider, or else their passkey on this request: a pending request returns a problem with `next.action` `confirm`; repeat it in the same client after confirming. And it must be older than the caller's own sign-in, so a fresh sign-in always wins.","operationId":"revokeLoginToken","parameters":[{"description":"The token's `id`, from the list.","example":1,"in":"path","name":"id","required":true,"schema":{"format":"int64","type":"integer"}}],"responses":{"204":{"description":"Revoked.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated","description":"Not signed in."},"403":{"$ref":"#/components/responses/Forbidden","description":"The caller is an agent or a runner, the token is no older than the caller's sign-in, or the request waits for the caller's passkey."},"404":{"$ref":"#/components/responses/NotFound","description":"The caller has no such token that still works."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"summary":"Revoke one of your CLI logins or browser sessions","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"invalid_request","statuses":[400]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true}},"/api/v1/auth/web-link":{"post":{"description":"Creates a one-time browser sign-in link. People only, which `cscale web` uses, with a CLI token or a phone's; a browser's session can't ask, so no session outlives the CLI login it came from. The link works once, for two minutes; its code sits in the URL's fragment, so it never reaches a server's logs. Its page names the account and signs the browser in only when the person presses Sign in. The session it starts ends with the token that asked for it.","operationId":"createWebLink","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"example":{},"schema":{"$ref":"#/components/schemas/WebLinkRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"expires_in_s":900,"url":"https://commitscale.com/login/web#code=web-sign-in-code"},"schema":{"$ref":"#/components/schemas/WebLink"}}},"description":"The link.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The caller is not a person, or is a browser's session."},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest","description":"`next` is not a path on this server."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"summary":"A one-time sign-in link for a browser","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"quota_exceeded","statuses":[422,429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"expired_token","statuses":[400]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/auth/web-link/check":{"post":{"description":"Shows which account a browser sign-in link is for. For the web app's own sign-in page only: the request's `Origin` must be this server. Says which account the code from a `cscale web` link signs the browser in to, without using it, so the page shows that and signs in only when the person presses Sign in. Limited per address.","operationId":"checkWebLink","parameters":[{"description":"This server's origin. Required for this browser sign-in step.","example":"https://commitscale.com","in":"header","name":"Origin","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"example":{"code":"web-sign-in-code"},"schema":{"$ref":"#/components/schemas/WebSessionRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"account":"alex"},"schema":{"$ref":"#/components/schemas/WebLinkInfo"}}},"description":"The account the link signs in to.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest","description":"The link expired or was used."},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The request did not come from this server's own page."},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"security":[{}],"summary":"Which account a sign-in link is for","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"quota_exceeded","statuses":[429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"expired_token","statuses":[400]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/auth/web-session":{"post":{"description":"Exchanges a browser sign-in link for a session. For the web app's own sign-in page only: the request's `Origin` must be this server. Exchanges the code from a `cscale web` link for the session cookie. The page first shows which account the link is for (`POST /api/v1/auth/web-link/check`), and calls this only when the person presses Sign in.","operationId":"createWebSession","parameters":[{"description":"This server's origin. Required for this browser sign-in step.","example":"https://commitscale.com","in":"header","name":"Origin","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"example":{"code":"web-sign-in-code"},"schema":{"$ref":"#/components/schemas/WebSessionRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"handle":"user:alex","is_admin":false,"kind":"user","name":"alex"},"schema":{"$ref":"#/components/schemas/Actor"}}},"description":"Signed in; the session cookie is set.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest","description":"The link expired or was used."},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The request did not come from this server's own page."},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"security":[{}],"summary":"Exchange a sign-in link for a session","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"quota_exceeded","statuses":[429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"expired_token","statuses":[400]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/billing":{"get":{"description":"Reports your personal plan and subscription. People only. Requires billing configuration.","operationId":"getBilling","responses":{"200":{"content":{"application/json":{"example":{"owns_subscription":false,"plan":"free","redirect_hosts":["billing.commitscale.com"],"subscription":{"cancel_at_period_end":false,"id":"sub_019a1f40","period_end":1,"shape_problems":[],"status":"active"}},"schema":{"$ref":"#/components/schemas/BillingStatus"}}},"description":"Plan and subscription.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/Conflict"},"410":{"$ref":"#/components/responses/ReplayExpired"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"summary":"Your personal Pro subscription","tags":["billing"],"x-commitscale-check-key":false,"x-commitscale-feature":"billing_pages","x-commitscale-problems":[{"code":"billing_not_configured","statuses":[503]},{"code":"billing_unavailable","statuses":[503]},{"code":"billing_refused","statuses":[400,403,404,405,409,410,415,422,429]},{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/billing/checkout":{"post":{"description":"Starts a personal Pro purchase. People only. Requires Idempotency-Key. Uses the account's verified email and trusted Billing return URLs.","operationId":"createBillingCheckout","parameters":[{"$ref":"#/components/parameters/BillingIdempotencyKey"}],"requestBody":{"content":{"application/json":{"example":{"seats":1},"schema":{"$ref":"#/components/schemas/BillingSeats"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"url":"https://billing.commitscale.com/checkout"},"schema":{"$ref":"#/components/schemas/BillingLink"}}},"description":"Checkout link.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/Conflict"},"410":{"$ref":"#/components/responses/ReplayExpired"},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"summary":"Buy Pro for yourself","tags":["billing"],"x-commitscale-check-key":false,"x-commitscale-feature":"billing_pages","x-commitscale-problems":[{"code":"billing_not_configured","statuses":[503]},{"code":"billing_unavailable","statuses":[503]},{"code":"billing_refused","statuses":[400,403,404,405,409,410,415,422,429]},{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"quota_exceeded","statuses":[429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/billing/portal":{"post":{"description":"Returns a link to the buyer's customer portal. Signed-in browser only, subscription buyer only. Requires Idempotency-Key. Returns to Billing.","operationId":"createBillingPortal","parameters":[{"description":"This server's origin. Required with the browser session cookie.","example":"https://commitscale.com","in":"header","name":"Origin","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/BillingIdempotencyKey"}],"responses":{"200":{"content":{"application/json":{"example":{"url":"https://billing.commitscale.com/checkout"},"schema":{"$ref":"#/components/schemas/BillingLink"}}},"description":"Portal link.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/Conflict"},"410":{"$ref":"#/components/responses/ReplayExpired"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"security":[{"session":[]}],"summary":"Open the buyer's customer portal","tags":["billing"],"x-commitscale-check-key":false,"x-commitscale-feature":"billing_pages","x-commitscale-problems":[{"code":"billing_not_configured","statuses":[503]},{"code":"billing_unavailable","statuses":[503]},{"code":"billing_refused","statuses":[400,403,404,405,409,410,415,422,429]},{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"invalid_request","statuses":[400]},{"code":"quota_exceeded","statuses":[429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/billing/requests/resolve":{"post":{"description":"Resolves an unfinished personal purchase. People only. Requires a fresh Idempotency-Key. Resolves only the acting person's request, without another purchase.","operationId":"resolveBillingRequest","parameters":[{"$ref":"#/components/parameters/BillingIdempotencyKey"}],"requestBody":{"content":{"application/json":{"example":{"key":"019a1f40-7c00-7000-8000-000000000003"},"schema":{"$ref":"#/components/schemas/BillingResolve"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"key":"019a1f40-7c00-7000-8000-000000000003","resolved":false,"status":0},"schema":{"$ref":"#/components/schemas/BillingResolved"}}},"description":"Resolution recorded; retry the original request.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/Conflict"},"410":{"$ref":"#/components/responses/ReplayExpired"},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"summary":"Resolve an unfinished purchase","tags":["billing"],"x-commitscale-check-key":false,"x-commitscale-feature":"billing_pages","x-commitscale-problems":[{"code":"billing_not_configured","statuses":[503]},{"code":"billing_unavailable","statuses":[503]},{"code":"billing_refused","statuses":[400,403,404,405,409,410,415,422,429]},{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"quota_exceeded","statuses":[429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/check-keys":{"get":{"description":"Lists your agents' check-only keys. People only, their own keys. Includes names, the owning agent, creation, expiry, revocation and last use (updated at most once a minute). Keys awaiting collection have no expiry yet. Never returns a secret. Keys belong only to their owner.","operationId":"listCheckKeys","responses":{"200":{"content":{"application/json":{"example":{"keys":[{"agent":"agent:check-1@alex","created_at":"2026-10-26T10:00:00Z","expires_at":"2026-10-26T10:00:00Z","id":1,"last_used_at":"2026-10-26T10:00:00Z","name":"My coding agent","revoked_at":"2026-10-26T10:00:00Z"}]},"schema":{"$ref":"#/components/schemas/CheckKeyList"}}},"description":"Your keys, newest first.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"summary":"List your agents' check-only keys","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true},"post":{"description":"Creates a check-only key for another agent. The person's CLI sign-in or a browser session signed in within 10 minutes can create a named key while their passkey layer is off. Phone tokens and browser API tokens cannot. With the layer on, returns passkey_layer_required; use the confirmed route. Creates an owned agent with no repository grants. The key lasts 90 days and is shown once, never stored or replayed. No caller can widen or refresh it. Broader agent powers retain their passkey requirement.","operationId":"createCheckKey","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"example":{"name":"My coding agent"},"schema":{"$ref":"#/components/schemas/NameCheckKey"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"key":{"agent":"agent:check-1@alex","created_at":"2026-10-26T10:00:00Z","expires_at":"2026-10-26T10:00:00Z","id":1,"last_used_at":"2026-10-26T10:00:00Z","name":"My coding agent","revoked_at":"2026-10-26T10:00:00Z"},"token":"cs_chk_example"},"schema":{"$ref":"#/components/schemas/CheckKeyIssued"}}},"description":"The key, shown once.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"People only. The optional passkey layer requires the confirmed route."},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"summary":"Give another agent a check-only key","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"passkey_layer_required","statuses":[403]},{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"quota_exceeded","statuses":[429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/check-keys/confirmed":{"post":{"description":"Creates a check-only key with passkey confirmation. People only. First returns a confirmation link. Confirm with the account's own passkey, then repeat the same name with the same sign-in token within fifteen minutes. Approval is consumed once. Creates the same owned agent without repository grants and the same 90-day check-only key as browser creation. Only the requesting client receives the secret; it is never stored or replayed. A declined, expired or consumed confirmation cannot issue a key.","operationId":"createConfirmedCheckKey","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"example":{"name":"My coding agent"},"schema":{"$ref":"#/components/schemas/NameCheckKey"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"key":{"agent":"agent:check-1@alex","created_at":"2026-10-26T10:00:00Z","expires_at":"2026-10-26T10:00:00Z","id":1,"last_used_at":"2026-10-26T10:00:00Z","name":"My coding agent","revoked_at":"2026-10-26T10:00:00Z"},"token":"cs_chk_example"},"schema":{"$ref":"#/components/schemas/CheckKeyIssued"}}},"description":"The key, returned once.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"A person must confirm this exact request with their passkey."},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"summary":"Create a check key with your passkey","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"already_exists","statuses":[409]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"quota_exceeded","statuses":[429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/check-keys/current/usage":{"get":{"description":"Returns a check-only key's daily API usage. Authenticates only a check-only key. The key ID is taken from its credential, never a path or caller-supplied ID. Returns daily API request counts including this read, newest first. These are not model usage or billing units; metering and billing add those separately.","operationId":"getCheckKeyUsage","parameters":[{"description":"How many days back, up to 366; 30 when not given.","example":1,"in":"query","name":"days","required":false,"schema":{"format":"int32","type":"integer"}}],"responses":{"200":{"content":{"application/json":{"example":{"days":[{"api_requests":1,"day":"2026-10-26"}],"key_id":1},"schema":{"$ref":"#/components/schemas/CheckKeyUsage"}}},"description":"This key's usage.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The caller is not a check-only key."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"summary":"Read this check-only key's own usage","tags":["auth"],"x-commitscale-check-key":true,"x-commitscale-feature":"usage","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"invalid_request","statuses":[400]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true}},"/api/v1/check-keys/{id}":{"delete":{"description":"Revokes one of your check-only keys. People only, their own keys. Revokes immediately, including a verified key not yet collected. A repeated revocation succeeds. The next authenticated request is refused; work already admitted cannot be recalled. No passkey is needed to remove this power.","operationId":"revokeCheckKey","parameters":[{"description":"The key ID from your list.","example":1,"in":"path","name":"id","required":true,"schema":{"format":"int64","type":"integer"}}],"responses":{"204":{"description":"Revoked.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound","description":"No such key owned by the caller."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"summary":"Revoke one of your check-only keys","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"invalid_request","statuses":[400]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true},"put":{"description":"Changes a check-only key's display name. People only, their own keys. Updates only the display name, never the agent's identity or powers.","operationId":"nameCheckKey","parameters":[{"description":"The key ID from your list.","example":1,"in":"path","name":"id","required":true,"schema":{"format":"int64","type":"integer"}}],"requestBody":{"content":{"application/json":{"example":{"name":"My coding agent"},"schema":{"$ref":"#/components/schemas/NameCheckKey"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"agent":"agent:check-1@alex","created_at":"2026-10-26T10:00:00Z","expires_at":"2026-10-26T10:00:00Z","id":1,"last_used_at":"2026-10-26T10:00:00Z","name":"My coding agent","revoked_at":"2026-10-26T10:00:00Z"},"schema":{"$ref":"#/components/schemas/CheckKey"}}},"description":"The renamed key.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound","description":"No such key owned by the caller."},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"summary":"Name one of your check-only keys","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true}},"/api/v1/invites":{"get":{"description":"Lists pending organization invitations. Organization owners read pending invitations by supplying org. Newest first.","operationId":"listInvites","parameters":[{"description":"The organization whose pending invitations to read.","example":"acme","in":"query","name":"org","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"invites":[{"created_at":"2026-10-26T10:00:00Z","email":"person@example.com","expires_at":"2026-10-26T10:00:00Z","id":1,"invited_by":"user:alex","org":"acme","person":"user:alex"}]},"schema":{"$ref":"#/components/schemas/InviteList"}}},"description":"The invites that wait.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The caller may not read them."},"404":{"$ref":"#/components/responses/NotFound","description":"No such organization."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"summary":"Invites that wait","tags":["invites"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"invalid_request","statuses":[400]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true},"post":{"description":"Invites someone to an organization by email. Organization owners invite someone by email from a signed-in browser. Supply org and a role of member or owner. An owner's invitation requires a sign-in within the last ten minutes or a passkey confirmation; follow next.url and repeat after confirming. The emailed link works once for seven days, creating an account or joining from an existing account. Invitation limits may apply.","operationId":"createInvite","parameters":[{"description":"This server's origin. Required with the browser session cookie.","example":"https://commitscale.com","in":"header","name":"Origin","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"example":{"email":"person@example.com","org":"acme"},"schema":{"$ref":"#/components/schemas/CreateInviteRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"example":{"created_at":"2026-10-26T10:00:00Z","email":"person@example.com","expires_at":"2026-10-26T10:00:00Z","id":1,"invited_by":"user:alex","org":"acme","person":"user:alex"},"schema":{"$ref":"#/components/schemas/Invite"}}},"description":"Sent.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The caller may not invite there, an owner's invite waits for their passkey, or the organization's Free plan cannot add another person."},"404":{"$ref":"#/components/responses/NotFound","description":"No such organization, or no such early-access request for the address."},"409":{"$ref":"#/components/responses/Conflict","description":"An invite to the address waits already, or is being sent; or, for the server alone, an account uses it."},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest","description":"The address is not valid."},"429":{"$ref":"#/components/responses/TooManyRequests","description":"Too many invites in the last day, from the sender, to the address, or from the server."},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy","description":"This server sends no email, or the email could not be sent; no invite was kept."}},"security":[{"session":[]}],"summary":"Invite someone by email","tags":["invites"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"organization_person_limit","statuses":[403]},{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"already_exists","statuses":[409]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"quota_exceeded","statuses":[422,429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/invites/mine":{"get":{"description":"Lists your pending organization invitations. People only. Invites to organizations that wait for the caller, newest first: those an owner sent them by their handle, and those sent to the address their account uses. It leaves out those they declined, those sent before they last left the organization, which no longer join them, and those to organizations they belong to already. The inbox lists them too, as `invites`. The caller joins only by accepting one.","operationId":"listMyInvites","responses":{"200":{"content":{"application/json":{"example":{"invites":[{"created_at":"2026-10-26T10:00:00Z","email":"person@example.com","expires_at":"2026-10-26T10:00:00Z","id":1,"invited_by":"user:alex","org":"acme","person":"user:alex"}]},"schema":{"$ref":"#/components/schemas/InviteList"}}},"description":"The invites.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The caller is not a person."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"summary":"Invites that wait for you","tags":["invites"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true}},"/api/v1/invites/{id}":{"delete":{"description":"Withdraws an invitation. The invitation's sender or an organization owner can withdraw it. Its link stops working at once.","operationId":"withdrawInvite","parameters":[{"description":"The invite.","example":1,"in":"path","name":"id","required":true,"schema":{"format":"int64","type":"integer"}}],"responses":{"204":{"description":"Withdrawn.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound","description":"No invite that waits, that the caller may withdraw."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"summary":"Withdraw an invite","tags":["invites"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"invalid_request","statuses":[400]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true}},"/api/v1/invites/{id}/accept":{"post":{"description":"Accepts an invitation to an organization. Only the person it is for, from their own session, or their own CLI or phone token; never an agent. Joins its organization at the role it offers. Someone in it already is told so, and nothing is recorded. Becoming an owner also needs a browser the caller signed in to with an email link, a passkey or a provider, or else their passkey on this request: a pending request returns a problem with `next.action` `confirm` and `next.url`; repeat it in the same client after confirming. Accepting an invite sent to the caller's address shows its organization which account is theirs.","operationId":"acceptMyInvite","parameters":[{"description":"The invite.","example":1,"in":"path","name":"id","required":true,"schema":{"format":"int64","type":"integer"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"200":{"content":{"application/json":{"example":{"created_at":"2026-10-26T10:00:00Z","name":"acme"},"schema":{"$ref":"#/components/schemas/OrgSummary"}}},"description":"Joined: the organization, and the caller's role in it.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The caller is not a person, becoming an owner waits for their passkey, or the organization's Free plan cannot add another person."},"404":{"$ref":"#/components/responses/NotFound","description":"No invite that waits for the caller."},"409":{"$ref":"#/components/responses/Conflict","description":"The caller is in the organization already."},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"summary":"Accept your organization invite","tags":["invites"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"organization_person_limit","statuses":[403]},{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"already_exists","statuses":[409]},{"code":"invalid_request","statuses":[400]},{"code":"quota_exceeded","statuses":[429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/invites/{id}/decline":{"post":{"description":"Declines an invitation to an organization. Only the person it is for, from their own session, or their own CLI or phone token; never an agent. An invite an owner sent them by their handle is withdrawn: its link stops working, and its organization's record says they declined it. One sent to their address only leaves their inbox: it waits, unanswered, until it expires, and only their own record says so, as `invite.dismissed`, so the organization never learns whose address it was.","operationId":"declineMyInvite","parameters":[{"description":"The invite.","example":1,"in":"path","name":"id","required":true,"schema":{"format":"int64","type":"integer"}},{"$ref":"#/components/parameters/IdempotencyKey"}],"responses":{"204":{"description":"Declined.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The caller is not a person."},"404":{"$ref":"#/components/responses/NotFound","description":"No invite that waits for the caller."},"409":{"$ref":"#/components/responses/Conflict"},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"summary":"Decline an invite","tags":["invites"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"invalid_request","statuses":[400]},{"code":"quota_exceeded","statuses":[429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/namespaces/{ns}/plan":{"get":{"description":"Returns the namespace's effective plan and public feature flags. Members may read them; a check-only key cannot call this operation. Missing or unknown flags are off. Manage your subscription from Billing.","operationId":"getNamespacePlan","parameters":[{"description":"A person, such as `john`, or an organization, such as `acme`.","example":"alex","in":"path","name":"ns","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"billing_available":false,"can_test_plan":false,"flags":{"account_pages":true,"accounts":true,"billing_pages":true,"model_api":true,"plan_pages":true,"remote_mcp":true,"usage":true,"usage_pages":true},"frozen":false,"namespace":"alex","people":1,"plan":"free"},"schema":{"$ref":"#/components/schemas/NamespacePlan"}}},"description":"The plan and its visible feature flags.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound","description":"No namespace the caller belongs to."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"summary":"The namespace's plan and feature flags","tags":["orgs"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"invalid_request","statuses":[400]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true}},"/api/v1/namespaces/{ns}/usage":{"get":{"description":"Returns usage counters and the personal workspace's check summary. API requests and bytes, model checks and reserved traffic counters, for each UTC day, latest first. model_checks gives the personal workspace's current UTC month summary, cap and reset time; Free counts checks in Free workspaces across verified accounts sharing an inbox. It is null for organizations. Personal usage lists your checks this month in each workspace you belong to. People and their delegated keys read their own namespace; organization owners read their organization's use. Today's counts include the last minute.","operationId":"getUsage","parameters":[{"description":"A person, such as `john`, or an organization, such as `acme`.","example":"alex","in":"path","name":"ns","required":true,"schema":{"type":"string"}},{"description":"How many days back, up to 366; 30 when not given.","example":1,"in":"query","name":"days","required":false,"schema":{"format":"int32","type":"integer"}}],"responses":{"200":{"content":{"application/json":{"example":{"days":[{"api_out_bytes":1,"api_requests":1,"day":"2026-10-26","git_in_bytes":1,"git_ms":1,"git_out_bytes":1,"log_bytes":1,"model_checks":1,"repo_bytes":1,"session_bytes":1}],"model_checks":{"cap":100,"resets_at":"2026-11-01T00:00:00Z","used":12},"namespace":"alex"},"schema":{"$ref":"#/components/schemas/UsageList"}}},"description":"Its days.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The caller belongs to the organization, but does not own it."},"404":{"$ref":"#/components/responses/NotFound","description":"No such namespace that the caller belongs to."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"summary":"What a namespace used, day by day","tags":["orgs"],"x-commitscale-check-key":true,"x-commitscale-feature":"usage","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"invalid_request","statuses":[400]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true}},"/api/v1/oauth/grants":{"get":{"description":"Lists your active connected apps, their approved scopes and when they were last used. A person's browser or CLI sign-in is required.","operationId":"listOAuthGrants","responses":{"200":{"content":{"application/json":{"example":{"grants":[{"client_host":"example","client_name":"example","created_at":"2026-10-26T10:00:00Z","id":1,"last_used_at":"2026-10-26T10:00:00Z","scopes":["example"]}]},"schema":{"$ref":"#/components/schemas/ConnectedApps"}}},"description":"Your active connected apps.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"A browser or CLI sign-in is required."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"summary":"List your connected apps","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true}},"/api/v1/oauth/grants/{id}":{"delete":{"description":"Ends one of your connected apps and all its tokens. A person's browser or CLI sign-in is required.","operationId":"revokeOAuthGrant","parameters":[{"description":"The connected app's grant ID.","example":1,"in":"path","name":"id","required":true,"schema":{"format":"int64","type":"integer"}}],"responses":{"204":{"description":"The connection and its tokens ended.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"A browser or CLI sign-in is required."},"404":{"$ref":"#/components/responses/NotFound","description":"The grant was not found."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"summary":"Revoke a connected app","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"invalid_request","statuses":[400]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true}},"/api/v1/openapi/public.json":{"get":{"description":"OpenAPI 3.1 for the operations included by Free or Pro. Follows this server's current plan grants, refreshed within 30 seconds.","operationId":"getPublicOpenApi","responses":{"200":{"content":{"application/json":{"example":{},"schema":{"type":"object"}}},"description":"The filtered OpenAPI document.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"security":[{}],"summary":"The public API description","tags":["server"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true}},"/api/v1/orgs":{"get":{"description":"Lists your organizations and your role in each. People only: the organizations they belong to, and their role in each.","operationId":"listOrgs","responses":{"200":{"content":{"application/json":{"example":{"orgs":[{"created_at":"2026-10-26T10:00:00Z","name":"acme"}]},"schema":{"$ref":"#/components/schemas/OrgList"}}},"description":"The organizations.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The caller is not a person."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"summary":"The caller's organizations","tags":["orgs"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true},"post":{"description":"Creates an organization. People only. The caller becomes its owner. Its name is a namespace, like a person's handle.","operationId":"createOrg","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"example":{"name":"acme"},"schema":{"$ref":"#/components/schemas/CreateOrgRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"example":{"created_at":"2026-10-26T10:00:00Z","members":[{"added_at":"2026-10-26T10:00:00Z","handle":"user:sam","name":"sam","role":"owner","teams":[]}],"name":"acme","settings":{},"teams":[]},"schema":{"$ref":"#/components/schemas/Org"}}},"description":"The organization.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The caller is not a person."},"409":{"$ref":"#/components/responses/Conflict","description":"The name is taken."},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest","description":"The name is not valid."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"summary":"Create an organization","tags":["orgs"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"already_exists","statuses":[409]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"quota_exceeded","statuses":[422,429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/orgs/{org}":{"get":{"description":"Returns an organization's members and their roles. People who belong to the organization can read its members and their roles.","operationId":"getOrg","parameters":[{"description":"The organization, such as `acme`.","example":"acme","in":"path","name":"org","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"created_at":"2026-10-26T10:00:00Z","members":[{"added_at":"2026-10-26T10:00:00Z","handle":"user:sam","name":"sam","role":"owner","teams":[]}],"name":"acme","settings":{},"teams":[]},"schema":{"$ref":"#/components/schemas/Org"}}},"description":"The organization.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The caller is not a person."},"404":{"$ref":"#/components/responses/NotFound","description":"No such organization that the caller belongs to."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"summary":"An organization","tags":["orgs"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"invalid_request","statuses":[400]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true}},"/api/v1/orgs/{org}/billing":{"get":{"description":"Shows an organization's Pro subscription. Only current owners in a signed-in browser. Shows the current people, price and subscription.","operationId":"getOrganizationBilling","parameters":[{"description":"Organization name","example":"acme","in":"path","name":"org","required":true,"schema":{"type":"string"}},{"description":"This server's origin.","example":"https://commitscale.com","in":"header","name":"Origin","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"buyer_is_you":false,"buyer_left":false,"monthly_usd":1,"plan":"example","redirect_hosts":["example"],"seated":["example"],"seats":1,"subscription":{"cancel_at_period_end":false,"period_end":1,"shape_problems":["example"],"status":"example"}},"schema":{"$ref":"#/components/schemas/OrganizationBilling"}}},"description":"Organization billing.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"An owner and signed-in browser are required."},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/Conflict"},"410":{"$ref":"#/components/responses/ReplayExpired"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"security":[{"session":[]}],"summary":"Read an organization's Pro subscription","tags":["billing"],"x-commitscale-check-key":false,"x-commitscale-feature":"billing_pages","x-commitscale-problems":[{"code":"billing_not_configured","statuses":[503]},{"code":"billing_unavailable","statuses":[503]},{"code":"billing_refused","statuses":[400,403,404,405,409,410,415,422,429]},{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"invalid_request","statuses":[400]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/orgs/{org}/billing/checkout":{"post":{"description":"Starts a Pro purchase for an organization. Only an owner in a signed-in browser. Requires Idempotency-Key; the prefix org-members: is reserved. Current people determine the quantity.","operationId":"createOrganizationCheckout","parameters":[{"description":"Organization name","example":"acme","in":"path","name":"org","required":true,"schema":{"type":"string"}},{"description":"This server's origin.","example":"https://commitscale.com","in":"header","name":"Origin","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/BillingIdempotencyKey"}],"responses":{"200":{"content":{"application/json":{"example":{"url":"https://billing.commitscale.com/checkout"},"schema":{"$ref":"#/components/schemas/BillingLink"}}},"description":"Checkout link.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"An owner and signed-in browser are required."},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/Conflict"},"410":{"$ref":"#/components/responses/ReplayExpired"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"security":[{"session":[]}],"summary":"Start Pro for an organization's members","tags":["billing"],"x-commitscale-check-key":false,"x-commitscale-feature":"billing_pages","x-commitscale-problems":[{"code":"organization_frozen","statuses":[403]},{"code":"billing_not_configured","statuses":[503]},{"code":"billing_unavailable","statuses":[503]},{"code":"billing_refused","statuses":[400,403,404,405,409,410,415,422,429]},{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"invalid_request","statuses":[400]},{"code":"quota_exceeded","statuses":[429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/orgs/{org}/billing/checkout/resolve":{"post":{"description":"Current owners may check a pending checkout. Only the owner who started it receives its URL.","operationId":"resolveOrganizationCheckout","parameters":[{"description":"Organization name","example":"acme","in":"path","name":"org","required":true,"schema":{"type":"string"}},{"description":"This server's origin.","example":"https://commitscale.com","in":"header","name":"Origin","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"pending":false},"schema":{"$ref":"#/components/schemas/OrganizationCheckoutResolution"}}},"description":"Checkout state.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"An owner and signed-in browser are required."},"404":{"$ref":"#/components/responses/NotFound","description":"No accessible organization."},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/Conflict","description":"The checkout is still pending."},"410":{"$ref":"#/components/responses/ReplayExpired"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"security":[{"session":[]}],"summary":"Resume an organization's pending checkout","tags":["billing"],"x-commitscale-check-key":false,"x-commitscale-feature":"billing_pages","x-commitscale-problems":[{"code":"organization_frozen","statuses":[403]},{"code":"billing_not_configured","statuses":[503]},{"code":"billing_unavailable","statuses":[503]},{"code":"billing_refused","statuses":[400,403,404,405,409,410,415,422,429]},{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"already_exists","statuses":[409]},{"code":"invalid_request","statuses":[400]},{"code":"quota_exceeded","statuses":[429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/orgs/{org}/billing/portal":{"post":{"description":"Returns a link to manage an organization's subscription. Only a current owner in a signed-in browser. Requires Idempotency-Key; the prefix org-members: is reserved. Cards, invoices and cancellation are managed in the portal.","operationId":"createOrganizationPortal","parameters":[{"description":"Organization name","example":"acme","in":"path","name":"org","required":true,"schema":{"type":"string"}},{"description":"This server's origin.","example":"https://commitscale.com","in":"header","name":"Origin","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/BillingIdempotencyKey"}],"responses":{"200":{"content":{"application/json":{"example":{"url":"https://billing.commitscale.com/checkout"},"schema":{"$ref":"#/components/schemas/BillingLink"}}},"description":"Portal link.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"An owner and signed-in browser are required."},"404":{"$ref":"#/components/responses/NotFound"},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/Conflict"},"410":{"$ref":"#/components/responses/ReplayExpired"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"security":[{"session":[]}],"summary":"Manage an organization's subscription","tags":["billing"],"x-commitscale-check-key":false,"x-commitscale-feature":"billing_pages","x-commitscale-problems":[{"code":"organization_frozen","statuses":[403]},{"code":"billing_not_configured","statuses":[503]},{"code":"billing_unavailable","statuses":[503]},{"code":"billing_refused","statuses":[400,403,404,405,409,410,415,422,429]},{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"invalid_request","statuses":[400]},{"code":"quota_exceeded","statuses":[429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/orgs/{org}/billing/summary":{"get":{"description":"Shows an organization's plan and seat count. Current organization members can read this summary. Payment management remains owner-only.","operationId":"getOrganizationBillingSummary","parameters":[{"description":"Organization name","example":"acme","in":"path","name":"org","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"plan":"example","seats":1},"schema":{"$ref":"#/components/schemas/OrganizationBillingSummary"}}},"description":"Plan and member count.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"404":{"$ref":"#/components/responses/NotFound","description":"No accessible organization."},"405":{"$ref":"#/components/responses/MethodNotAllowed"},"409":{"$ref":"#/components/responses/Conflict"},"410":{"$ref":"#/components/responses/ReplayExpired"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"summary":"Read an organization's plan and seats","tags":["billing"],"x-commitscale-check-key":false,"x-commitscale-feature":"billing_pages","x-commitscale-problems":[{"code":"billing_not_configured","statuses":[503]},{"code":"billing_unavailable","statuses":[503]},{"code":"billing_refused","statuses":[400,403,404,405,409,410,415,422,429]},{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"invalid_request","statuses":[400]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/orgs/{org}/members":{"get":{"description":"Lists an organization's members. People only.","operationId":"listOrgMembers","parameters":[{"description":"The organization, such as `acme`.","example":"acme","in":"path","name":"org","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"members":[{"added_at":"2026-10-26T10:00:00Z","handle":"user:sam","name":"sam","role":"owner","teams":[]}]},"schema":{"$ref":"#/components/schemas/OrgMemberList"}}},"description":"The members.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The caller is not a person."},"404":{"$ref":"#/components/responses/NotFound","description":"No such organization that the caller belongs to."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"summary":"An organization's members","tags":["orgs"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"invalid_request","statuses":[400]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true}},"/api/v1/orgs/{org}/members/{user}":{"delete":{"description":"Removes a member from an organization. Owners remove a member; a member can remove themselves. The person and their agents lose access to the organization immediately.","operationId":"removeOrgMember","parameters":[{"description":"The organization, such as `acme`.","example":"acme","in":"path","name":"org","required":true,"schema":{"type":"string"}},{"description":"The person's handle, such as `bob`.","example":"sam","in":"path","name":"user","required":true,"schema":{"type":"string"}}],"responses":{"204":{"description":"Removed.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The caller is not an owner, and not removing themselves."},"404":{"$ref":"#/components/responses/NotFound","description":"No such organization, person, or membership."},"409":{"$ref":"#/components/responses/Conflict","description":"It would leave the organization without an owner."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"summary":"Remove a member","tags":["orgs"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"organization_frozen","statuses":[403]},{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"conflict","statuses":[409]},{"code":"invalid_request","statuses":[400]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true},"put":{"description":"Invites a person or changes a member's role. The organization's owners only. Someone who isn't a member is invited, not added: the invite goes to their account's address, which the answer never shows, and waits in their inbox, and they join, at the role it offers, only when they accept it. A member's new role applies at once. Without a role, the invite is to be a member, and a member keeps their role. Inviting someone, or making a member an owner, needs a browser the caller signed in to with an email link, a passkey or a provider, or else their passkey on this request: a pending request returns a problem with `next.action` `confirm` and `next.url`; repeat it in the same client after confirming. For an owner, that browser must have signed in within the last 10 minutes. An organization always keeps at least one owner. A frozen organization cannot promote a member to owner; demotions remain available.","operationId":"setOrgMember","parameters":[{"description":"The organization, such as `acme`.","example":"acme","in":"path","name":"org","required":true,"schema":{"type":"string"}},{"description":"The person's handle, such as `bob`.","example":"sam","in":"path","name":"user","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application/json":{"example":{},"schema":{"oneOf":[{"type":"null"},{"$ref":"#/components/schemas/SetMemberRequest"}]}}},"description":"Optional."},"responses":{"200":{"content":{"application/json":{"example":{"added_at":"2026-10-26T10:00:00Z","handle":"user:sam","name":"sam","role":"owner","teams":[]},"schema":{"$ref":"#/components/schemas/OrgMember"}}},"description":"The member, with their role.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"202":{"content":{"application/json":{"example":{"created_at":"2026-10-26T10:00:00Z","email":"person@example.com","expires_at":"2026-10-26T10:00:00Z","id":1,"invited_by":"user:alex","org":"acme","person":"user:alex"},"schema":{"$ref":"#/components/schemas/Invite"}}},"description":"Invited: they join when they accept.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The caller is not an owner, the request waits for their passkey, the organization's Free plan cannot add another person, or the organization is frozen and the request would promote an owner."},"404":{"$ref":"#/components/responses/NotFound","description":"No such organization or person."},"409":{"$ref":"#/components/responses/Conflict","description":"It would leave the organization without an owner, an invite to them waits already, or their membership changed meanwhile."},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest","description":"The caller invited themselves."},"429":{"$ref":"#/components/responses/TooManyRequests","description":"Too many invites in the last day."},"500":{"$ref":"#/components/responses/Internal"}},"summary":"Invite a person, or change a member's role","tags":["orgs"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"organization_frozen","statuses":[403]},{"code":"organization_person_limit","statuses":[403]},{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"already_exists","statuses":[409]},{"code":"conflict","statuses":[409]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"quota_exceeded","statuses":[422,429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true}},"/api/v1/support":{"post":{"description":"Sends a request for help to support. Any signed-in caller on any plan, including agents and check-only keys. The request is kept and a person replies to the account's email: the caller's own, or its owner's. The message is never logged. Email delivery is queued and retried. At most 20 requests per account in an hour and 10,000 across the server in 30 days protect the server; follow Retry-After when it is full.","operationId":"contactSupport","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"example":{"cli_version":"0.1.0","message":"My test check cannot finish. How can I retry it?"},"schema":{"$ref":"#/components/schemas/SupportRequest"}}},"required":true},"responses":{"201":{"content":{"application/json":{"example":{"id":1,"reply_to":"alex@example.com"},"schema":{"$ref":"#/components/schemas/SupportReceived"}}},"description":"Stored, with the address the reply goes to.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated","description":"Not signed in."},"403":{"$ref":"#/components/responses/Forbidden"},"409":{"$ref":"#/components/responses/Conflict"},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest","description":"The message or client details are invalid."},"429":{"$ref":"#/components/responses/TooManyRequests","description":"The server's support protection limit was reached."},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"summary":"Ask a person for help","tags":["support"],"x-commitscale-check-key":true,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"support_limited","statuses":[429]},{"code":"quota_exceeded","statuses":[422,429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/tests/acceptance-key":{"get":{"description":"Returns the server's acceptance public key. Pin this key and server with each trusted person on the target branch. Publication does not grant trust.","operationId":"getTestAcceptanceKey","responses":{"200":{"content":{"application/json":{"example":{"keyid":"acceptance-2026-10","public_key":"11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo=","server":"https://commitscale.com"},"schema":{"$ref":"#/components/schemas/TestAcceptanceKey"}}},"description":"The current public key.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"security":[{}],"summary":"Read the server's acceptance public key","tags":["tests"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true}},"/api/v1/tests/acceptance-policy":{"get":{"description":"Reports whether your test acceptances need a passkey. Sign-ins, agents and check keys read only their own responsible person's effective rule. CI uses its protected check credential. Runner and job credentials cannot substitute their owner for the person whose check is being verified.","operationId":"getTestAcceptancePolicy","responses":{"200":{"content":{"application/json":{"example":{"passkey_required":false,"person":"user:alex"},"schema":{"$ref":"#/components/schemas/TestAcceptancePolicy"}}},"description":"The effective acceptance rule.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"Use the check's credential."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"summary":"Read whether your test acceptances need a passkey","tags":["tests"],"x-commitscale-check-key":true,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true}},"/api/v1/tests/acceptances":{"post":{"description":"Send the protected signer request produced by the CLI for an in-toto test-acceptance statement. The payload and its DSSE pre-authentication encoding must agree and name the signed-in person and current acceptance key. Open confirmation.url for that person to review and confirm with their passkey. Poll the returned confirmation ID at GET /api/v1/tests/acceptances/{id}; only the confirmed, exact bytes get a signed envelope. The request expires after five minutes.","operationId":"requestTestAcceptance","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"example":{"by":"user:alex","keyid":"acceptance-2026-10","pae":"RFNTRXYxIDI4IGFwcGxpY2F0aW9uL3ZuZC5pbi10b3RvK2pzb24gNDMgeyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEifQ==","payload":"eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEifQ==","payloadType":"application/vnd.in-toto+json","purpose":"test-acceptance","version":1},"schema":{"$ref":"#/components/schemas/SignAcceptanceRequest"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"confirmation":{"action":"create_check_key","confirmed_at":"2026-10-26T10:00:00Z","created_at":"2026-10-26T10:00:00Z","expires_at":"2026-10-26T10:00:00Z","id":"019a1f40-7c00-7000-8000-000000000004","person":"user:alex","title":"Create a check key","url":"https://commitscale.com/confirm/019a1f40-7c00-7000-8000-000000000004"},"envelope":{"payload":"eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEifQ==","payloadType":"application/vnd.in-toto+json","signatures":[{"keyid":"acceptance-2026-10","sig":"AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAhIiMkJSYnKCkqKywtLi8wMTIzNDU2Nzg5Ojs8PT4/QA=="}]}},"schema":{"$ref":"#/components/schemas/TestAcceptanceSigning"}}},"description":"Waiting for your passkey, for five minutes.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"People only, naming their own handle and the current key."},"409":{"$ref":"#/components/responses/Conflict","description":"Five acceptance confirmations are already pending."},"413":{"$ref":"#/components/responses/TooLarge"},"415":{"$ref":"#/components/responses/UnsupportedMediaType"},"422":{"$ref":"#/components/responses/InvalidRequest","description":"Not a valid acceptance statement."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy"}},"summary":"Ask to sign your test acceptance with a passkey","tags":["tests"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"already_exists","statuses":[409]},{"code":"invalid_request","statuses":[400,413,415,422]},{"code":"quota_exceeded","statuses":[422,429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/tests/acceptances/{id}":{"get":{"description":"Reads only your own acceptance request. The envelope is null until you confirm the exact statement with a passkey. A completed request returns the same signed envelope on every read. Publication of the verification key alone does not make an acceptance trusted.","operationId":"getTestAcceptance","parameters":[{"description":"Confirmation ID returned when requesting the acceptance.","example":"019a1f40-7c00-7000-8000-000000000004","in":"path","name":"id","required":true,"schema":{"type":"string"}}],"responses":{"200":{"content":{"application/json":{"example":{"confirmation":{"action":"create_check_key","confirmed_at":"2026-10-26T10:00:00Z","created_at":"2026-10-26T10:00:00Z","expires_at":"2026-10-26T10:00:00Z","id":"019a1f40-7c00-7000-8000-000000000004","person":"user:alex","title":"Create a check key","url":"https://commitscale.com/confirm/019a1f40-7c00-7000-8000-000000000004"},"envelope":{"payload":"eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEifQ==","payloadType":"application/vnd.in-toto+json","signatures":[{"keyid":"acceptance-2026-10","sig":"AQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHyAhIiMkJSYnKCkqKywtLi8wMTIzNDU2Nzg5Ojs8PT4/QA=="}]}},"schema":{"$ref":"#/components/schemas/TestAcceptanceSigning"}}},"description":"The same envelope on every read after confirmation.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"People only."},"404":{"$ref":"#/components/responses/NotFound","description":"Not your acceptance."},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"summary":"Read your test acceptance","tags":["tests"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"invalid_request","statuses":[400]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true}},"/api/v1/tests/checks":{"post":{"description":"Starts a local test check's model questions. Requires sign-in and model_api. Send a random check_id, protocol_version v1, cli_version, an Idempotency-Key and optionally the repository's workspace; omit workspace for your own. You must belong to the workspace. Starting counts one check against that workspace. Free allows 100 checks per UTC month across your Free workspaces and verified addresses sharing an inbox; other plans have no cap. Exact retries replay for 24 hours without counting again. A configured model is required. A frozen organization refuses new checks.","operationId":"startTestCheck","parameters":[{"$ref":"#/components/parameters/TestModelIdempotencyKey"}],"requestBody":{"content":{"application/json":{"example":{"check_id":"019a1f40-7c00-7000-8000-000000000001","cli_version":"0.1.0","protocol_version":"v1"},"schema":{"$ref":"#/components/schemas/StartTestCheck"}}},"required":true},"responses":{"201":{"content":{"application/json":{"example":{"check_id":"019a1f40-7c00-7000-8000-000000000001","protocol_version":"v1"},"schema":{"$ref":"#/components/schemas/TestCheckStarted"}}},"description":"Check started, or its original answer replayed.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The organization is frozen, the Free allowance is used, or model_api is off."},"404":{"$ref":"#/components/responses/NotFound","description":"The workspace is not available to the caller."},"409":{"$ref":"#/components/responses/Conflict"},"410":{"$ref":"#/components/responses/ReplayExpired","description":"The replay window expired."},"413":{"$ref":"#/components/responses/TooLarge","description":"The body exceeds 64 KiB."},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy","description":"The model is not configured or the workspace is temporarily busy."}},"summary":"Start a local test check's model questions","tags":["tests"],"x-commitscale-check-key":true,"x-commitscale-feature":"model_api","x-commitscale-problems":[{"code":"organization_frozen","statuses":[403]},{"code":"model_check_cap_reached","statuses":[403]},{"code":"model_unavailable","statuses":[503]},{"code":"excerpt_too_large","statuses":[413]},{"code":"model_replay_expired","statuses":[410]},{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"invalid_request","statuses":[400,413,422]},{"code":"quota_exceeded","statuses":[429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/tests/checks/{check_id}/questions":{"post":{"description":"Asks one of a local check's model questions. Requires sign-in, model_api and an Idempotency-Key. Questions of a started check do not count again and continue through cap, month and plan changes, within the replay window and question bound. Older uncounted checks count when their first question receives a place. Queue mode runs up to four questions per person at once across their keys, agents and checks.\n\nSix input kinds have exact v17 prompts. The service scrubs again, rejects oversized input without shortening it, and keeps IDs, hashes and scores for replay. The configured prompt token limit also determines the byte limits for the rendered prompt, the total input fields and the question body; stored answers can replay after those limits are lowered. Only Free accounts that opt in to sharing data to help improve Commitscale keep scrubbed input fields in a separate store; withdrawal deletes them. Responses echo IDs.\n\nRetries must preserve the exact body, request_id and key. An expired replay never starts another charge. A model_unavailable problem leaves the question unanswered. Answer Yes means p_yes >= 0.5; it does not apply the CLI's policy thresholds.","operationId":"askTestQuestion","parameters":[{"description":"The check's random UUID.","example":"019a1f40-7c00-7000-8000-000000000001","in":"path","name":"check_id","required":true,"schema":{"type":"string"}},{"$ref":"#/components/parameters/TestModelIdempotencyKey"}],"requestBody":{"content":{"application/json":{"example":{"candidate_id":"candidate:1","input":{"hunk":"@@ -1 +1 @@\n-return total;\n+return total + tax;\n","kind":"hunk","path":"src/total.rs"},"prompt_version":"v17-layout-1","question":"behaviour","redaction_version":"1","request_id":"019a1f40-7c00-7000-8000-000000000002"},"schema":{"$ref":"#/components/schemas/AskTestQuestion"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"answer":"Yes","candidate_id":"candidate:1","check_id":"019a1f40-7c00-7000-8000-000000000001","checkpoint":"v17-f16:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef","model_version":"v17-f16","p_yes":0.98,"prompt_sha256":"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef","prompt_version":"v17-layout-1","question":"behaviour","request_id":"019a1f40-7c00-7000-8000-000000000002","template_version":"cscale-1-chat-v1","tokenizer_version":"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"},"schema":{"$ref":"#/components/schemas/TestQuestionAnswered"}}},"description":"First-token Yes/No probability, or the stored answer.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"$ref":"#/components/responses/BadRequest"},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden","description":"The Free mailbox's check allowance is used, or model_api is off."},"404":{"$ref":"#/components/responses/NotFound"},"409":{"$ref":"#/components/responses/Conflict","description":"The same question is already queued or running."},"410":{"$ref":"#/components/responses/ReplayExpired","description":"The replay window expired."},"413":{"$ref":"#/components/responses/TooLarge","description":"An input bound was exceeded."},"422":{"$ref":"#/components/responses/InvalidRequest"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"},"503":{"$ref":"#/components/responses/Busy","description":"The model is unavailable."}},"summary":"Ask one of the local check's five model questions","tags":["tests"],"x-commitscale-check-key":true,"x-commitscale-feature":"model_api","x-commitscale-problems":[{"code":"model_check_cap_reached","statuses":[403]},{"code":"model_unavailable","statuses":[503]},{"code":"excerpt_too_large","statuses":[413]},{"code":"model_replay_expired","statuses":[410]},{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"not_found","statuses":[404]},{"code":"invalid_request","statuses":[400,413,422]},{"code":"quota_exceeded","statuses":[429]},{"code":"idempotency_key_reused","statuses":[409,422]},{"code":"idempotency_key_in_use","statuses":[409]},{"code":"internal","statuses":[500]},{"code":"unavailable","statuses":[503]}],"x-commitscale-public":true}},"/api/v1/whoami":{"get":{"description":"The person, agent, or runner whose credentials these are, and when the token expires.","operationId":"whoami","responses":{"200":{"content":{"application/json":{"example":{"handle":"user:alex","is_admin":false,"kind":"user","name":"alex"},"schema":{"$ref":"#/components/schemas/Actor"}}},"description":"The caller.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"401":{"$ref":"#/components/responses/Unauthenticated"},"403":{"$ref":"#/components/responses/Forbidden"},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"summary":"Who the caller is","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"accounts","x-commitscale-problems":[{"code":"check_key_scope","statuses":[403]},{"code":"feature_off","statuses":[403]},{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true}},"/mcp":{"post":{"description":"Send one JSON-RPC message. Initialize, discovery and the tool list work signed out; the first tool call challenges for a sign-in. Connected apps use OAuth access tokens bound to this resource. Personal tokens, agent tokens and check-only keys retain their API rights. The server returns JSON without sessions or SSE. Protocol errors use JSON-RPC; authentication and rate-limit errors use problem JSON. Supported protocol versions are 2026-07-28, 2025-11-25 and 2025-06-18. A browser origin must match this server; cookies are not used.","operationId":"remoteMcp","requestBody":{"content":{"application/json":{"example":{},"schema":{}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"id":1,"jsonrpc":"2.0","result":{"tools":[]}},"schema":{}}},"description":"A JSON-RPC result or error.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"202":{"description":"A notification was accepted.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"content":{"application/json":{"example":{"error":{"code":-32022,"data":{"requested":"2024-11-05","supported":["2026-07-28","2025-11-25","2025-06-18"]},"message":"Unsupported protocol version"},"id":1,"jsonrpc":"2.0"},"schema":{}}},"description":"Invalid JSON-RPC or protocol headers.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"401":{"$ref":"#/components/responses/Unauthenticated","description":"Sign in or replace an invalid credential."},"403":{"$ref":"#/components/responses/Forbidden","description":"The origin is not allowed or the connected app needs a broader scope."},"404":{"content":{"application/json":{"example":{},"schema":{}}},"description":"The modern protocol method is unknown.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"413":{"content":{"text/plain":{"schema":{"type":"string"}}},"description":"The message exceeds the request body limit.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"security":[{}],"summary":"Call the remote MCP server","tags":["server"],"x-commitscale-check-key":true,"x-commitscale-feature":"remote_mcp","x-commitscale-problems":[{"code":"unauthenticated","statuses":[401]},{"code":"invalid_token","statuses":[401]},{"code":"forbidden","statuses":[403]},{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true}},"/mcp/server-card":{"get":{"description":"Returns the public server card when Free or Pro includes remote MCP, or 404 before then. The card names the endpoint and supported protocol versions. It has no tool list. It is cacheable with ETag and supports cross-origin reads without credentials.","operationId":"mcpServerCard","responses":{"200":{"content":{"application/mcp-server-card+json":{"example":{"$schema":"https://static.modelcontextprotocol.io/schemas/v1/server-card.schema.json","description":"Tools for working on software hosted by Commitscale.","name":"com.commitscale/mcp","remotes":[{"supportedProtocolVersions":["2026-07-28","2025-11-25","2025-06-18"],"type":"streamable-http","url":"https://example.com/mcp"}],"title":"Commitscale","version":"0.1.0"},"schema":{"$ref":"#/components/schemas/ServerCard"}}},"description":"The public MCP server card.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"304":{"description":"The card has not changed.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"404":{"description":"The remote server is not yet public.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"$ref":"#/components/responses/Internal"}},"security":[{}],"summary":"Discover the remote MCP server","tags":["server"],"x-commitscale-check-key":false,"x-commitscale-feature":"remote_mcp","x-commitscale-problems":[{"code":"quota_exceeded","statuses":[429]},{"code":"internal","statuses":[500]}],"x-commitscale-public":true}},"/oauth/authorize":{"get":{"description":"Starts a ten-minute consent request for the MCP resource. Send client_id, redirect_uri, response_type=code, code_challenge and code_challenge_method=S256. Optional scope defaults to read; write includes read. Optional resource defaults to this server's /mcp. State is returned unchanged. Only approval redirects automatically; authorization errors are HTML and show a Back link once the client and redirect are verified.","operationId":"startOAuthAuthorization","parameters":[{"description":"Registered client ID or client metadata URL.","example":"example","in":"query","name":"client_id","required":true,"schema":{"type":"string"}},{"description":"A registered redirect URI.","example":"example","in":"query","name":"redirect_uri","required":true,"schema":{"type":"string"}},{"description":"Must be code.","example":"example","in":"query","name":"response_type","required":true,"schema":{"type":"string"}},{"description":"The S256 PKCE challenge.","example":"example","in":"query","name":"code_challenge","required":true,"schema":{"type":"string"}},{"description":"Must be S256.","example":"example","in":"query","name":"code_challenge_method","required":true,"schema":{"type":"string"}},{"description":"Space-separated read, write and offline_access.","example":"example","in":"query","name":"scope","required":false,"schema":{"type":"string"}},{"description":"Client state, at most 2 KiB.","example":"example","in":"query","name":"state","required":false,"schema":{"type":"string"}},{"description":"The issuer's /mcp resource.","example":"example","in":"query","name":"resource","required":false,"schema":{"type":"string"}},{"description":"If supplied, must be query.","example":"example","in":"query","name":"response_mode","required":false,"schema":{"type":"string"}}],"responses":{"303":{"description":"Continue to Commitscale's consent page.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"content":{"text/html":{"schema":{"type":"string"}}},"description":"The request is not valid.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"content":{"text/html":{"schema":{"type":"string"}}},"description":"Authorization is unavailable.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}}},"security":[{}],"summary":"Start an OAuth authorization","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"remote_mcp","x-commitscale-problems":[{"code":"quota_exceeded","statuses":[429]}],"x-commitscale-public":true}},"/oauth/register":{"post":{"description":"Registers a public client with one to ten HTTPS or loopback HTTP redirect URIs. Only authorization_code and refresh_token grants, the code response and token_endpoint_auth_method none are supported. Client names are self-declared and cannot contain control, format, tag, line or paragraph separator characters or double quotes. Registrations that never obtain a grant are swept after one day unless a live authorization request names them. Registration errors use OAuth client-registration JSON. A 429 can be an OAuth error from registration limits or a problem from the shared address limit; both carry Retry-After.","operationId":"registerOAuthClient","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/json":{"example":{"redirect_uris":["example"]},"schema":{"$ref":"#/components/schemas/Registration"}}},"required":true},"responses":{"201":{"content":{"application/json":{"example":{"application_type":"example","client_id":"example","client_id_issued_at":1,"client_name":"example","grant_types":["example"],"redirect_uris":["example"],"response_types":["example"],"token_endpoint_auth_method":"example"},"schema":{"$ref":"#/components/schemas/Registered"}}},"description":"The registered client metadata.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"content":{"application/json":{"example":{"error":"example","error_description":"example"},"schema":{"$ref":"#/components/schemas/OAuthError"}}},"description":"Invalid client metadata or redirect URI.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"429":{"content":{"application/json":{"example":{"error":"invalid_client_metadata","error_description":"Too many registrations."},"schema":{"$ref":"#/components/schemas/OAuthError"}},"application/problem+json":{"example":{"code":"quota_exceeded","detail":"ip:203.0.113.1 has used its 600 requests for this minute.","next":{"action":"retry","after_s":1},"retry_after_s":1,"status":429,"title":"Quota reached","type":"https://commitscale.com/errors/quota-exceeded"},"schema":{"$ref":"#/components/schemas/Problem"}}},"description":"Registration limits return OAuth JSON; the shared address limit returns problem JSON. Both include Retry-After.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"},"Retry-After":{"$ref":"#/components/headers/Retry-After"}}},"500":{"content":{"application/json":{"example":{"error":"example","error_description":"example"},"schema":{"$ref":"#/components/schemas/OAuthError"}}},"description":"Registration is unavailable.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}}},"security":[{}],"summary":"Register a public OAuth client","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"remote_mcp","x-commitscale-problems":[{"code":"quota_exceeded","statuses":[429]}],"x-commitscale-public":true}},"/oauth/revoke":{"post":{"description":"Send a form-encoded token and optionally client_id. Revoking an access token ends that token; revoking a refresh token ends its grant and every token in it, and records the client as the actor. Unknown and already-ended tokens answer 200.","operationId":"revokeOAuthToken","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"type":"string"}}},"required":true},"responses":{"200":{"description":"The token is no longer usable, or was unknown.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"content":{"application/json":{"example":{"error":"example","error_description":"example"},"schema":{"$ref":"#/components/schemas/OAuthError"}}},"description":"The revocation request is invalid.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"content":{"application/json":{"example":{"error":"example","error_description":"example"},"schema":{"$ref":"#/components/schemas/OAuthError"}}},"description":"The OAuth endpoint is unavailable.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}}},"security":[{}],"summary":"Revoke an OAuth token","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"remote_mcp","x-commitscale-problems":[{"code":"quota_exceeded","statuses":[429]}],"x-commitscale-public":true}},"/oauth/token":{"post":{"description":"Send an application/x-www-form-urlencoded request. For authorization_code, send client_id, code and code_verifier, and optionally redirect_uri and resource. For refresh_token, send client_id and refresh_token, and optionally resource. Codes expire after 60 seconds and refresh tokens rotate on use. A reused code revokes its grant only when its client, redirect, resource and verifier pass their checks, including after expiry. The answer is never cached. Errors use OAuth JSON, including invalid_request, invalid_client, invalid_grant, invalid_target, unsupported_grant_type and invalid_scope.","operationId":"exchangeOAuthToken","parameters":[{"$ref":"#/components/parameters/IdempotencyKey"}],"requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"type":"string"}}},"required":true},"responses":{"200":{"content":{"application/json":{"example":{"access_token":"example","expires_in":0,"refresh_token":"example","scope":"example","token_type":"example"},"schema":{"$ref":"#/components/schemas/OAuthTokenResponse"}}},"description":"An access token and rotated refresh token.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"400":{"content":{"application/json":{"example":{"error":"example","error_description":"example"},"schema":{"$ref":"#/components/schemas/OAuthError"}}},"description":"The OAuth request or grant was not accepted.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}},"429":{"$ref":"#/components/responses/TooManyRequests"},"500":{"content":{"application/json":{"example":{"error":"example","error_description":"example"},"schema":{"$ref":"#/components/schemas/OAuthError"}}},"description":"The OAuth endpoint is unavailable.","headers":{"Commitscale-Web-Build":{"$ref":"#/components/headers/Commitscale-Web-Build"},"Idempotent-Replayed":{"$ref":"#/components/headers/Idempotent-Replayed"},"RateLimit":{"$ref":"#/components/headers/RateLimit"},"RateLimit-Policy":{"$ref":"#/components/headers/RateLimit-Policy"}}}},"security":[{}],"summary":"Exchange or refresh an OAuth token","tags":["auth"],"x-commitscale-check-key":false,"x-commitscale-feature":"remote_mcp","x-commitscale-problems":[{"code":"quota_exceeded","statuses":[429]}],"x-commitscale-public":true}}},"security":[{"token":[]},{"session":[]}],"servers":[{"url":"https://commitscale.com"}],"tags":[{"description":"Pro for people and organizations.","name":"billing"},{"description":"Local test checks: model questions, durable replay, metered admissions and passkey-confirmed acceptances.","name":"tests"},{"description":"The API description for your tools.","name":"server"},{"description":"Account credentials, browser sessions and sign-in.","name":"auth"},{"description":"Organizations and their members.","name":"orgs"},{"description":"Contact support from your account.","name":"support"},{"description":"Invite people to your organization by email. People join by accepting an invitation.","name":"invites"}],"x-commitscale-problems":{"access_denied":{"action":"Stop. After five wrong codes, start a new login; otherwise, sign in another way.","description":"A device login was denied or refused after five wrong codes, or the passkey does not sign in here."},"already_exists":{"action":"Use the one that exists, or choose another name.","description":"Something with that name exists, or its current state does not allow this request."},"authorization_pending":{"action":"Poll again after `Retry-After` seconds.","description":"A device login or agent sign-up is waiting for the person's approval or email verification."},"billing_not_configured":{"action":"Billing is unavailable on this server. Try again later or contact support.","description":"Billing is not configured on this server or its billing service."},"billing_refused":{"action":"Review Billing and retry uncertain operations with the original key. Resolve an unfinished personal purchase through billing/requests/resolve. New work after a final refusal needs a fresh key.","description":"Billing refused the operation. billing_code names its reason; pending_key identifies a purchase needing resolution."},"billing_unavailable":{"action":"Retry the same input with the same idempotency key. Never start another purchase to recover a lost reply.","description":"Billing could not confirm the request."},"check_key_scope":{"action":"Use a person's sign-in for account management. More agent power needs the owner's passkey; this key cannot widen itself.","description":"A check-only key may call the test check, read its own usage and acceptance rule, contact support, and withdraw data sharing."},"conflict":{"action":"Read `detail`, fetch the current state, and change the request. When `retry_after_s` is set, retrying the same request later can work.","description":"The request conflicts with the current state, such as a membership that changed since you read it."},"excerpt_too_large":{"action":"Skip the question and leave it unanswered. Do not shorten a diff, split its meaning across questions or retry the same oversized input.","description":"A model request exceeds a field, line, rendered byte, token, body or internal per-check question bound."},"expired_token":{"action":"Start a new flow. If a sign-up already created the account but its key was lost, the person signs in to revoke and replace that key.","description":"A device login code, sign-up, sign-in link, or passkey prompt expired or was already used."},"feature_off":{"action":"Retrying on the same plan will not help. GET /api/v1/namespaces/{namespace}/plan, and the namespace's Plan page in Settings, show what its plan includes.","description":"The namespace plan does not include this feature."},"forbidden":{"action":"Stop. Retrying will not help. Ask the owner for the access, or leave the step to a person.","description":"The caller may not do this: only people may, only owners may, or the agent was not given this repository or write access."},"idempotency_key_in_use":{"action":"When `Retry-After` is set, retry then with the same key. Otherwise the first request succeeded: do not repeat it.","description":"The first request with this key is still running, including a queued model question, or it succeeded with a secret, such as a new token, which is never replayed."},"idempotency_key_reused":{"action":"Use a new key for each new request, and the same key only for a retry of the same request.","description":"The idempotency key was already used for a different request."},"internal":{"action":"Retry after `Retry-After` seconds. A write is safe to retry with the same idempotency key.","description":"The server failed, and logged why."},"invalid_request":{"action":"Fix the request as `detail` says. The same request fails the same way.","description":"The request is malformed or breaks a rule, such as a title that is too long, or a body that is not the expected JSON."},"invalid_token":{"action":"Stop using it. Sign in again, or ask the owner to create a replacement agent key.","description":"The token is unknown, expired, or revoked, its agent was revoked, or an OAuth token was used outside its MCP resource."},"model_check_cap_reached":{"action":"Do not retry automatically. Open next.url to upgrade the workspace to Pro and keep checking. Already started checks may finish.","description":"The Free allowance of 100 checks this UTC calendar month is used across Free workspaces. Verified addresses that reach one inbox share the allowance. The check was not started."},"model_replay_expired":{"action":"Leave this question unanswered. Never silently substitute a fresh check for an expired retry.","description":"A model check's 24-hour replay window expired. Its retained identities cannot be charged again."},"model_unavailable":{"action":"Leave the question unanswered. Retry the exact body with the same check ID, request ID and idempotency key within the remaining check budget.","description":"The local check's model is not configured or could not return both answer probabilities."},"not_found":{"action":"Check the name or ID, and that the token has the repository.","description":"It does not exist, or the caller cannot read it: a repository the caller cannot read is reported as not found."},"organization_frozen":{"action":"An owner can renew Pro or remove people until one remains; others can ask an owner.","description":"The organization's Free plan has more than one person; members cannot write, nobody can promote an owner, and no check can start."},"organization_person_limit":{"action":"An owner can upgrade the organization to Pro, or ask an owner to do so.","description":"The organization's Free plan allows one person."},"passkey_layer_required":{"action":"Run cscale key create to confirm with a passkey, or use POST /api/v1/check-keys/confirmed.","description":"The person enabled the passkey layer. Direct check-key creation is refused."},"quota_exceeded":{"action":"With 429, wait `Retry-After` seconds and retry. With 422, remove one first.","description":"With 429, the caller used its requests for the minute. With 422, a count limit was reached."},"signup_closed":{"action":"Ask for an invite, or sign in to an existing account.","description":"Open sign-up is turned off on this server."},"signup_limited":{"action":"Wait Retry-After seconds before starting again. Keep polling an existing request if you have one.","description":"The address or network reached its rolling sign-up start limit."},"slow_down":{"action":"Wait `Retry-After` seconds, and poll less often.","description":"A device login or agent sign-up was polled too often."},"support_limited":{"action":"Wait `Retry-After` seconds and retry.","description":"The account or server has sent too many recent support requests."},"unauthenticated":{"action":"Use this operation's sign-in method. Bearer credentials use `Authorization: Bearer <token>`; browser credentials use the session cookie.","description":"The request carried no credentials."},"unavailable":{"action":"With 502, retry later, or sign in another way. With 503, retry after `Retry-After` seconds, using the same idempotency key for a write; a long-running write can then replay its answer.","description":"With 502, a service the server needs did not answer, such as a sign-in provider. With 503, the server is busy, such as when a workspace is locked or host lookups are full, or a write with an idempotency key runs long: that write goes on, and its answer is kept for the key."}}}