{"openapi":"3.1.0","info":{"title":"KaamKa API","description":"REST API for KaamKa — India hyperlocal physical task marketplace. Versioned API (current v1). Version via URL path `/api/v1/*` or header `X-API-Version: 1`. Deprecation signaled via `Deprecation` + `Sunset` headers with 6-month timeline. Auth via Supabase JWT (Bearer) or OAuth 2.0 (authorization_code, scopes: read:tasks write:tasks read:messages write:messages). RateLimit headers on all responses; Retry-After on 429.","version":"1.0.0","contact":{"name":"KaamKa Support","email":"sankalp75shambharkar@gmail.com","url":"https://kaamka.vercel.app/contact"}},"servers":[{"url":"https://kaamka.vercel.app","description":"Production (v1)"},{"url":"https://kaamka.vercel.app/api/v1","description":"Versioned v1"}],"paths":{"/api/health":{"get":{"operationId":"healthCheck","summary":"Health check","description":"Public health check — no auth, no scopes. Use to verify API reachability and version.","tags":["Health"],"security":[],"parameters":[{"name":"X-API-Version","in":"header","required":false,"schema":{"type":"string","enum":["1","1.0.0"],"default":"1"},"description":"API version"}],"responses":{"200":{"description":"Service is healthy","headers":{"RateLimit-Limit":{"schema":{"type":"string"},"description":"Requests per minute"},"RateLimit-Remaining":{"schema":{"type":"string"}},"Deprecation":{"schema":{"type":"string"},"description":"If present, endpoint is deprecated"},"Sunset":{"schema":{"type":"string"},"description":"RFC 8594 Sunset date"}},"content":{"application/json":{"schema":{"type":"object","required":["status","service","version","timestamp"],"properties":{"status":{"type":"string","example":"ok"},"service":{"type":"string","example":"KaamKa API"},"version":{"type":"string","example":"1.0.0"},"timestamp":{"type":"string","format":"date-time"}}}}}}}}},"/api/agreements":{"post":{"operationId":"createAgreement","summary":"Lock a deal","description":"Lock a deal between poster and doer. Requires write:tasks scope.","tags":["Agreements"],"security":[{"bearerAuth":["write:tasks"]},{"oauth2":["write:tasks"]}],"parameters":[{"name":"X-API-Version","in":"header","required":false,"schema":{"type":"string","enum":["1"],"default":"1"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["chatId","offerAmt"],"properties":{"chatId":{"type":"string","description":"Private chat ID"},"offerAmt":{"type":"number","description":"Agreed price INR","minimum":1}}}}}},"responses":{"200":{"description":"Agreement created","headers":{"RateLimit-Limit":{"schema":{"type":"string"}},"RateLimit-Remaining":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"agreementId":{"type":"string"}}}}}},"400":{"description":"Invalid request","content":{"application/json":{"schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","description":"Machine-readable error code","example":400},"error":{"type":"string","description":"Error type","example":"Bad Request"},"message":{"type":"string","description":"Human-readable message","example":"Invalid request body"},"resolution":{"type":"string","description":"How to fix","example":"Check request fields and retry"}}}},"application/problem+json":{"schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","description":"Machine-readable error code","example":400},"error":{"type":"string","description":"Error type","example":"Bad Request"},"message":{"type":"string","description":"Human-readable message","example":"Invalid request body"},"resolution":{"type":"string","description":"How to fix","example":"Check request fields and retry"}}}}}},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","description":"Machine-readable error code","example":400},"error":{"type":"string","description":"Error type","example":"Bad Request"},"message":{"type":"string","description":"Human-readable message","example":"Invalid request body"},"resolution":{"type":"string","description":"How to fix","example":"Check request fields and retry"}}}}}},"403":{"description":"Not participant","content":{"application/json":{"schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","description":"Machine-readable error code","example":400},"error":{"type":"string","description":"Error type","example":"Bad Request"},"message":{"type":"string","description":"Human-readable message","example":"Invalid request body"},"resolution":{"type":"string","description":"How to fix","example":"Check request fields and retry"}}}}}},"409":{"description":"No longer available","content":{"application/json":{"schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","description":"Machine-readable error code","example":400},"error":{"type":"string","description":"Error type","example":"Bad Request"},"message":{"type":"string","description":"Human-readable message","example":"Invalid request body"},"resolution":{"type":"string","description":"How to fix","example":"Check request fields and retry"}}}}}},"429":{"description":"Rate limited","headers":{"Retry-After":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","description":"Machine-readable error code","example":400},"error":{"type":"string","description":"Error type","example":"Bad Request"},"message":{"type":"string","description":"Human-readable message","example":"Invalid request body"},"resolution":{"type":"string","description":"How to fix","example":"Check request fields and retry"}}}}}}}}},"/api/messages":{"post":{"operationId":"sendMessage","summary":"Send a message","description":"Send text/offer/counter. Requires write:messages scope. Contact details blocked pre-lock.","tags":["Messages"],"security":[{"bearerAuth":["write:messages"]},{"oauth2":["write:messages"]}],"parameters":[{"name":"X-API-Version","in":"header","required":false,"schema":{"type":"string","enum":["1"],"default":"1"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["chatId","content","msgType"],"properties":{"chatId":{"type":"string"},"content":{"type":"string"},"msgType":{"type":"string","enum":["text","offer","counter"]},"offerAmt":{"type":"number","minimum":1}}}}}},"responses":{"200":{"description":"Message sent","headers":{"RateLimit-Limit":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"blocked":{"type":"boolean"}}}}}},"400":{"description":"Invalid","content":{"application/json":{"schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","description":"Machine-readable error code","example":400},"error":{"type":"string","description":"Error type","example":"Bad Request"},"message":{"type":"string","description":"Human-readable message","example":"Invalid request body"},"resolution":{"type":"string","description":"How to fix","example":"Check request fields and retry"}}}}}},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","description":"Machine-readable error code","example":400},"error":{"type":"string","description":"Error type","example":"Bad Request"},"message":{"type":"string","description":"Human-readable message","example":"Invalid request body"},"resolution":{"type":"string","description":"How to fix","example":"Check request fields and retry"}}}}}},"403":{"description":"Forbidden","content":{"application/json":{"schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","description":"Machine-readable error code","example":400},"error":{"type":"string","description":"Error type","example":"Bad Request"},"message":{"type":"string","description":"Human-readable message","example":"Invalid request body"},"resolution":{"type":"string","description":"How to fix","example":"Check request fields and retry"}}}}}},"404":{"description":"Chat not found","content":{"application/json":{"schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","description":"Machine-readable error code","example":400},"error":{"type":"string","description":"Error type","example":"Bad Request"},"message":{"type":"string","description":"Human-readable message","example":"Invalid request body"},"resolution":{"type":"string","description":"How to fix","example":"Check request fields and retry"}}}}}},"409":{"description":"Chat closed","content":{"application/json":{"schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","description":"Machine-readable error code","example":400},"error":{"type":"string","description":"Error type","example":"Bad Request"},"message":{"type":"string","description":"Human-readable message","example":"Invalid request body"},"resolution":{"type":"string","description":"How to fix","example":"Check request fields and retry"}}}}}},"429":{"description":"Rate limit 20/min","headers":{"Retry-After":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","description":"Machine-readable error code","example":400},"error":{"type":"string","description":"Error type","example":"Bad Request"},"message":{"type":"string","description":"Human-readable message","example":"Invalid request body"},"resolution":{"type":"string","description":"How to fix","example":"Check request fields and retry"}}}}}}}}},"/api/signoff":{"post":{"operationId":"signOffTask","summary":"Sign off a task","description":"Poster confirms completion. Requires write:tasks.","tags":["Tasks"],"security":[{"bearerAuth":["write:tasks"]},{"oauth2":["write:tasks"]}],"parameters":[{"name":"X-API-Version","in":"header","required":false,"schema":{"type":"string","enum":["1"],"default":"1"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["agreementId"],"properties":{"agreementId":{"type":"string"}}}}}},"responses":{"200":{"description":"Signed off","headers":{"RateLimit-Limit":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"paymentReleased":{"type":"boolean"}}}}}},"400":{"description":"Invalid","content":{"application/json":{"schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","description":"Machine-readable error code","example":400},"error":{"type":"string","description":"Error type","example":"Bad Request"},"message":{"type":"string","description":"Human-readable message","example":"Invalid request body"},"resolution":{"type":"string","description":"How to fix","example":"Check request fields and retry"}}}}}},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","description":"Machine-readable error code","example":400},"error":{"type":"string","description":"Error type","example":"Bad Request"},"message":{"type":"string","description":"Human-readable message","example":"Invalid request body"},"resolution":{"type":"string","description":"How to fix","example":"Check request fields and retry"}}}}}},"403":{"description":"Not poster","content":{"application/json":{"schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","description":"Machine-readable error code","example":400},"error":{"type":"string","description":"Error type","example":"Bad Request"},"message":{"type":"string","description":"Human-readable message","example":"Invalid request body"},"resolution":{"type":"string","description":"How to fix","example":"Check request fields and retry"}}}}}}}}},"/api/done":{"post":{"operationId":"markTaskDone","summary":"Mark task as done","description":"Doer marks checkpoints complete. Requires write:tasks.","tags":["Tasks"],"security":[{"bearerAuth":["write:tasks"]},{"oauth2":["write:tasks"]}],"parameters":[{"name":"X-API-Version","in":"header","required":false,"schema":{"type":"string","enum":["1"],"default":"1"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["agreementId"],"properties":{"agreementId":{"type":"string"}}}}}},"responses":{"200":{"description":"Marked done","headers":{"RateLimit-Limit":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}}}}}},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","description":"Machine-readable error code","example":400},"error":{"type":"string","description":"Error type","example":"Bad Request"},"message":{"type":"string","description":"Human-readable message","example":"Invalid request body"},"resolution":{"type":"string","description":"How to fix","example":"Check request fields and retry"}}}}}},"403":{"description":"Not doer","content":{"application/json":{"schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","description":"Machine-readable error code","example":400},"error":{"type":"string","description":"Error type","example":"Bad Request"},"message":{"type":"string","description":"Human-readable message","example":"Invalid request body"},"resolution":{"type":"string","description":"How to fix","example":"Check request fields and retry"}}}}}}}}},"/api/dispute":{"post":{"operationId":"raiseDispute","summary":"Raise a dispute","description":"Raise dispute, freeze funds. Requires write:tasks.","tags":["Disputes"],"security":[{"bearerAuth":["write:tasks"]},{"oauth2":["write:tasks"]}],"parameters":[{"name":"X-API-Version","in":"header","required":false,"schema":{"type":"string","enum":["1"],"default":"1"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["agreementId","reason"],"properties":{"agreementId":{"type":"string"},"reason":{"type":"string"}}}}}},"responses":{"200":{"description":"Dispute raised","headers":{"RateLimit-Limit":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"disputeId":{"type":"string"}}}}}},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","description":"Machine-readable error code","example":400},"error":{"type":"string","description":"Error type","example":"Bad Request"},"message":{"type":"string","description":"Human-readable message","example":"Invalid request body"},"resolution":{"type":"string","description":"How to fix","example":"Check request fields and retry"}}}}}},"403":{"description":"Not participant","content":{"application/json":{"schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","description":"Machine-readable error code","example":400},"error":{"type":"string","description":"Error type","example":"Bad Request"},"message":{"type":"string","description":"Human-readable message","example":"Invalid request body"},"resolution":{"type":"string","description":"How to fix","example":"Check request fields and retry"}}}}}}}}},"/api/upload":{"post":{"operationId":"getUploadUrl","summary":"Get presigned upload URL","description":"Get Cloudflare R2 presigned URL. Requires write:tasks.","tags":["Uploads"],"security":[{"bearerAuth":["write:tasks"]},{"oauth2":["write:tasks"]}],"parameters":[{"name":"X-API-Version","in":"header","required":false,"schema":{"type":"string","enum":["1"],"default":"1"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["filename","contentType"],"properties":{"filename":{"type":"string"},"contentType":{"type":"string"}}}}}},"responses":{"200":{"description":"Presigned URL","headers":{"RateLimit-Limit":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"uploadUrl":{"type":"string","format":"uri"},"fileUrl":{"type":"string","format":"uri"}}}}}},"400":{"description":"Invalid","content":{"application/json":{"schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","description":"Machine-readable error code","example":400},"error":{"type":"string","description":"Error type","example":"Bad Request"},"message":{"type":"string","description":"Human-readable message","example":"Invalid request body"},"resolution":{"type":"string","description":"How to fix","example":"Check request fields and retry"}}}}}},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","description":"Machine-readable error code","example":400},"error":{"type":"string","description":"Error type","example":"Bad Request"},"message":{"type":"string","description":"Human-readable message","example":"Invalid request body"},"resolution":{"type":"string","description":"How to fix","example":"Check request fields and retry"}}}}}},"429":{"description":"Rate limited","headers":{"Retry-After":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","description":"Machine-readable error code","example":400},"error":{"type":"string","description":"Error type","example":"Bad Request"},"message":{"type":"string","description":"Human-readable message","example":"Invalid request body"},"resolution":{"type":"string","description":"How to fix","example":"Check request fields and retry"}}}}}}}}},"/api/rehire":{"post":{"operationId":"rehireDoer","summary":"Rehire a doer","description":"Rehire from history. Requires write:tasks.","tags":["Tasks"],"security":[{"bearerAuth":["write:tasks"]},{"oauth2":["write:tasks"]}],"parameters":[{"name":"X-API-Version","in":"header","required":false,"schema":{"type":"string","enum":["1"],"default":"1"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["doerId","listingId"],"properties":{"doerId":{"type":"string"},"listingId":{"type":"string"}}}}}},"responses":{"200":{"description":"Rehire initiated","headers":{"RateLimit-Limit":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"},"chatId":{"type":"string"}}}}}},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","description":"Machine-readable error code","example":400},"error":{"type":"string","description":"Error type","example":"Bad Request"},"message":{"type":"string","description":"Human-readable message","example":"Invalid request body"},"resolution":{"type":"string","description":"How to fix","example":"Check request fields and retry"}}}}}}}}},"/api/calls/token":{"post":{"operationId":"getVideoCallToken","summary":"Get video call token","description":"Generate 100ms JWT. Requires read:tasks.","tags":["Calls"],"security":[{"bearerAuth":["read:tasks"]},{"oauth2":["read:tasks"]}],"parameters":[{"name":"X-API-Version","in":"header","required":false,"schema":{"type":"string","enum":["1"],"default":"1"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["agreementId"],"properties":{"agreementId":{"type":"string"}}}}}},"responses":{"200":{"description":"Video call token","headers":{"RateLimit-Limit":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"token":{"type":"string"},"roomId":{"type":"string"}}}}}},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","description":"Machine-readable error code","example":400},"error":{"type":"string","description":"Error type","example":"Bad Request"},"message":{"type":"string","description":"Human-readable message","example":"Invalid request body"},"resolution":{"type":"string","description":"How to fix","example":"Check request fields and retry"}}}}}},"429":{"description":"Rate limited","headers":{"Retry-After":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","description":"Machine-readable error code","example":400},"error":{"type":"string","description":"Error type","example":"Bad Request"},"message":{"type":"string","description":"Human-readable message","example":"Invalid request body"},"resolution":{"type":"string","description":"How to fix","example":"Check request fields and retry"}}}}}}}}},"/api/push/subscribe":{"post":{"operationId":"subscribePush","summary":"Subscribe to push","description":"Register web-push. Requires write:tasks.","tags":["Push"],"security":[{"bearerAuth":["write:tasks"]},{"oauth2":["write:tasks"]}],"parameters":[{"name":"X-API-Version","in":"header","required":false,"schema":{"type":"string","enum":["1"],"default":"1"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["subscription"],"properties":{"subscription":{"type":"object"}}}}}},"responses":{"200":{"description":"Subscribed","headers":{"RateLimit-Limit":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}}}}}},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","description":"Machine-readable error code","example":400},"error":{"type":"string","description":"Error type","example":"Bad Request"},"message":{"type":"string","description":"Human-readable message","example":"Invalid request body"},"resolution":{"type":"string","description":"How to fix","example":"Check request fields and retry"}}}}}}}},"delete":{"operationId":"unsubscribePush","summary":"Unsubscribe from push","description":"Remove subscription. Requires write:tasks.","tags":["Push"],"security":[{"bearerAuth":["write:tasks"]},{"oauth2":["write:tasks"]}],"parameters":[{"name":"X-API-Version","in":"header","required":false,"schema":{"type":"string","enum":["1"],"default":"1"}}],"responses":{"200":{"description":"Removed","headers":{"RateLimit-Limit":{"schema":{"type":"string"}}},"content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean"}}}}}},"401":{"description":"Not authenticated","content":{"application/json":{"schema":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","description":"Machine-readable error code","example":400},"error":{"type":"string","description":"Error type","example":"Bad Request"},"message":{"type":"string","description":"Human-readable message","example":"Invalid request body"},"resolution":{"type":"string","description":"How to fix","example":"Check request fields and retry"}}}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"Supabase JWT","description":"Supabase JWT. Scopes: read:tasks, write:tasks, read:messages, write:messages","x-scopes":["read:tasks","write:tasks","read:messages","write:messages"]},"oauth2":{"type":"oauth2","description":"OAuth 2.0 authorization_code with PKCE. Scopes for least-privilege. Discovery at /.well-known/oauth-authorization-server","flows":{"authorizationCode":{"authorizationUrl":"https://kaamka.vercel.app/auth/signup","tokenUrl":"https://kaamka.vercel.app/api/oauth/token","scopes":{"read:tasks":"Read tasks and agreements","write:tasks":"Create and update tasks","read:messages":"Read messages","write:messages":"Send messages"}}}}},"schemas":{"Error":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","description":"Machine-readable error code","example":400},"error":{"type":"string","description":"Error type","example":"Bad Request"},"message":{"type":"string","description":"Human-readable message","example":"Invalid request body"},"resolution":{"type":"string","description":"How to fix","example":"Check request fields and retry"}}},"ProblemJson":{"type":"object","required":["code","message"],"properties":{"code":{"type":"integer","description":"Machine-readable error code","example":400},"error":{"type":"string","description":"Error type","example":"Bad Request"},"message":{"type":"string","description":"Human-readable message","example":"Invalid request body"},"resolution":{"type":"string","description":"How to fix","example":"Check request fields and retry"}}}},"parameters":{"ApiVersion":{"name":"X-API-Version","in":"header","required":false,"schema":{"type":"string","enum":["1","1.0.0"],"default":"1"},"description":"API version. Current 1. Deprecated versions return Sunset/Deprecation headers."}},"headers":{"RateLimitLimit":{"schema":{"type":"string"},"description":"Requests per window"},"RateLimitRemaining":{"schema":{"type":"string"}},"RateLimitReset":{"schema":{"type":"string"}},"RetryAfter":{"schema":{"type":"string"}},"Deprecation":{"schema":{"type":"string"}},"Sunset":{"schema":{"type":"string"},"description":"RFC 8594"}}},"security":[{"bearerAuth":[],"oauth2":[]}]}