{"openapi":"3.1.0","info":{"title":"Testivora API","version":"1.0.0","description":"Programmatic access to your Testivora testimonials, spaces and walls. Authenticate with a Bearer API key created in Settings → API. Available on the Growth and Agency plans.","contact":{"name":"Testivora","url":"https://testivora.com"}},"servers":[{"url":"https://api.testivora.com","description":"Production"}],"security":[{"bearerAuth":[]}],"tags":[{"name":"Account"},{"name":"Spaces"},{"name":"Testimonials"},{"name":"Walls"}],"paths":{"/v1/account":{"get":{"tags":["Account"],"summary":"Retrieve the authenticated account","description":"Returns the account tier, API access flag, and current usage vs. plan limits.","responses":{"200":{"description":"The account object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Account"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Plan does not include API access, or key lacks the required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found in your account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/spaces":{"get":{"tags":["Spaces"],"summary":"List spaces","description":"Lists all spaces owned by the account (most recent first).","responses":{"200":{"description":"A list of spaces.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/SpaceList"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Plan does not include API access, or key lacks the required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found in your account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/spaces/{id}":{"get":{"tags":["Spaces"],"summary":"Retrieve a space","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The space object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Space"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Plan does not include API access, or key lacks the required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found in your account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/testimonials":{"get":{"tags":["Testimonials"],"summary":"List testimonials","description":"Lists testimonials within a single space (cursor-paginated, most recent first). `space_id` is required.","parameters":[{"name":"space_id","in":"query","required":true,"schema":{"type":"string"},"description":"Required. The space to list testimonials from."},{"name":"status","in":"query","required":false,"schema":{"type":"string","enum":["pending","approved","archived"]},"description":"Filter by status. Omit to return all statuses."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","minimum":1,"maximum":100,"default":25}},{"name":"cursor","in":"query","required":false,"schema":{"type":"string"},"description":"Pagination cursor from a previous response's next_cursor."}],"responses":{"200":{"description":"A page of testimonials.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/TestimonialList"}}}},"400":{"description":"Missing required space_id.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Plan does not include API access, or key lacks the required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found in your account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"post":{"tags":["Testimonials"],"summary":"Create a text testimonial","description":"Imports a text testimonial collected elsewhere. Counts against the account's monthly testimonial quota. Video testimonials are not creatable via the API in v1.","requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateTestimonial"}}}},"responses":{"201":{"description":"The created testimonial.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Testimonial"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"402":{"description":"Monthly quota for the plan exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Plan does not include API access, or key lacks the required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found in your account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/testimonials/{id}":{"get":{"tags":["Testimonials"],"summary":"Retrieve a testimonial","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The testimonial object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Testimonial"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Plan does not include API access, or key lacks the required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found in your account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}},"patch":{"tags":["Testimonials"],"summary":"Update a testimonial","description":"Update status (pending/approved/archived), featured flag, and/or tags.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateTestimonial"}}}},"responses":{"200":{"description":"The updated testimonial.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Testimonial"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Plan does not include API access, or key lacks the required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found in your account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"422":{"description":"Validation error.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/walls":{"get":{"tags":["Walls"],"summary":"List walls (or fetch one by space slug)","description":"Without query params: lists all walls (summaries). With `space_slug`: returns the published wall for that space, including its ordered testimonials.","parameters":[{"name":"space_slug","in":"query","required":false,"schema":{"type":"string"},"description":"If set, returns that space's published wall (full object)."}],"responses":{"200":{"description":"A list of wall summaries, or a single wall when space_slug is set."},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Plan does not include API access, or key lacks the required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found in your account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}},"/v1/walls/{id}":{"get":{"tags":["Walls"],"summary":"Retrieve a wall","description":"Returns the wall config plus its approved testimonials, already filtered and ordered per the wall's blocks settings.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"The wall object.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Wall"}}}},"401":{"description":"Missing or invalid API key.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"403":{"description":"Plan does not include API access, or key lacks the required scope.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"404":{"description":"Resource not found in your account.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}},"429":{"description":"Rate limit exceeded.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Error"}}}}}}}},"webhooks":{"testimonial.created":{"post":{"summary":"A testimonial was received (any status)","description":"Sent to your registered endpoints. Verify the Testivora-Signature header (HMAC-SHA256 of `${t}.${rawBody}`). Return 2xx to acknowledge.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Acknowledged."}}}},"testimonial.approved":{"post":{"summary":"A testimonial moved to approved","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Acknowledged."}}}},"testimonial.deleted":{"post":{"summary":"A testimonial was deleted","description":"data is { id, object, space_id, deleted: true }.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Acknowledged."}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"tv_live_…","description":"Your API key as `Authorization: Bearer tv_live_…`."}},"schemas":{"Error":{"type":"object","properties":{"error":{"type":"object","properties":{"type":{"type":"string","description":"Machine-readable error code."},"message":{"type":"string"}},"required":["type","message"]}}},"Testimonial":{"type":"object","properties":{"id":{"type":"string"},"object":{"type":"string","enum":["testimonial"]},"space_id":{"type":"string"},"status":{"type":"string","enum":["pending","approved","archived"]},"type":{"type":"string","enum":["video","text"]},"text":{"type":"string","nullable":true},"rating":{"type":"number","nullable":true,"minimum":1,"maximum":5},"featured":{"type":"boolean"},"tags":{"type":"array","items":{"type":"string"}},"highlight":{"type":"string","nullable":true},"power_quotes":{"type":"array","items":{"type":"string"}},"transcription":{"type":"string","nullable":true},"author":{"type":"object","properties":{"name":{"type":"string","nullable":true},"title":{"type":"string","nullable":true},"company":{"type":"string","nullable":true},"photo_url":{"type":"string","nullable":true,"description":"Signed URL (expires)."},"social":{"type":"object","nullable":true,"properties":{"linkedin":{"type":"string","nullable":true},"x":{"type":"string","nullable":true},"instagram":{"type":"string","nullable":true},"website":{"type":"string","nullable":true},"youtube":{"type":"string","nullable":true}}}}},"video":{"type":"object","nullable":true,"description":"Present only when type=video. URLs are signed and expire.","properties":{"url":{"type":"string","nullable":true},"thumbnail_url":{"type":"string","nullable":true},"camera_url":{"type":"string","nullable":true},"duration_seconds":{"type":"number","nullable":true},"content_type":{"type":"string","nullable":true}}},"images":{"type":"array","items":{"type":"object","properties":{"url":{"type":"string","nullable":true},"caption":{"type":"string","nullable":true}}}},"created_at":{"type":"string","format":"date-time","nullable":true},"published_at":{"type":"string","format":"date-time","nullable":true}}},"Space":{"type":"object","properties":{"id":{"type":"string"},"object":{"type":"string","enum":["space"]},"name":{"type":"string"},"slug":{"type":"string"},"description":{"type":"string","nullable":true},"primary_color":{"type":"string"},"background_style":{"type":"string","enum":["white","warm","dark"]},"logo_url":{"type":"string","nullable":true},"testimonial_count":{"type":"integer"},"approved_count":{"type":"integer"},"public_wall_url":{"type":"string"},"created_at":{"type":"string","format":"date-time","nullable":true}}},"Wall":{"type":"object","properties":{"id":{"type":"string"},"object":{"type":"string","enum":["wall"]},"space_id":{"type":"string"},"name":{"type":"string"},"slug":{"type":"string","nullable":true},"template":{"type":"string","enum":["editorial","mosaic","cinema","cohort","pulse"]},"published":{"type":"boolean"},"published_at":{"type":"string","format":"date-time","nullable":true},"public_url":{"type":"string","nullable":true},"theme":{"type":"object","nullable":true,"additionalProperties":true},"copy":{"type":"object","additionalProperties":true},"blocks":{"type":"object","additionalProperties":true},"testimonials":{"type":"array","items":{"$ref":"#/components/schemas/Testimonial"},"description":"Approved testimonials, already filtered + ordered per the wall's blocks config."}}},"Account":{"type":"object","properties":{"object":{"type":"string","enum":["account"]},"email":{"type":"string","nullable":true},"tier":{"type":"string"},"api_access":{"type":"boolean"},"period":{"type":"string","description":"Current usage period, YYYY-MM."},"usage":{"type":"object","additionalProperties":true},"limits":{"type":"object","additionalProperties":true,"description":"Plan limits; null means unlimited."}}},"TestimonialList":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"data":{"type":"array","items":{"$ref":"#/components/schemas/Testimonial"}},"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true}}},"SpaceList":{"type":"object","properties":{"object":{"type":"string","enum":["list"]},"data":{"type":"array","items":{"$ref":"#/components/schemas/Space"}},"has_more":{"type":"boolean"},"next_cursor":{"type":"string","nullable":true}}},"CreateTestimonial":{"type":"object","required":["space_id","text"],"properties":{"space_id":{"type":"string"},"text":{"type":"string","maxLength":10000},"rating":{"type":"number","minimum":1,"maximum":5},"status":{"type":"string","enum":["pending","approved"],"default":"approved"},"tags":{"type":"array","items":{"type":"string"}},"author":{"type":"object","properties":{"name":{"type":"string"},"title":{"type":"string"},"company":{"type":"string"},"photo_url":{"type":"string","description":"External image URL."},"email":{"type":"string"},"social":{"type":"object","nullable":true,"properties":{"linkedin":{"type":"string","nullable":true},"x":{"type":"string","nullable":true},"instagram":{"type":"string","nullable":true},"website":{"type":"string","nullable":true},"youtube":{"type":"string","nullable":true}}}}}}},"UpdateTestimonial":{"type":"object","properties":{"status":{"type":"string","enum":["pending","approved","archived"]},"featured":{"type":"boolean"},"tags":{"type":"array","items":{"type":"string"}}}},"WebhookEvent":{"type":"object","description":"Outbound webhook event envelope.","properties":{"id":{"type":"string","description":"evt_… (stable across retries; dedupe on it)."},"object":{"type":"string","enum":["event"]},"event":{"type":"string","enum":["testimonial.created","testimonial.approved","testimonial.deleted"]},"version":{"type":"string"},"created":{"type":"string","format":"date-time"},"attempt":{"type":"integer"},"data":{"$ref":"#/components/schemas/Testimonial"}}}}}}