{
  "openapi": "3.1.0",
  "info": {
    "title": "TempoRevenue API",
    "version": "1.0.0",
    "summary": "Create proposal drafts from client conversations and read their status.",
    "description": "Create proposal drafts in your TempoRevenue account from a client conversation, check whether the client opened or accepted them, and read your credit balance.\n\nProposals are always created as drafts. Nothing in this API sends anything to a client. Each created proposal uses one proposal from your plan or one purchased credit, the same as in the app.\n\nAuthenticate with an API key from Integrations in the app (Authorization: Bearer tr_live_...). The same key works for the TempoRevenue MCP server at https://app.temporevenue.com/api/mcp. Guides, limits and examples: https://temporevenue.com/developers/",
    "termsOfService": "https://app.temporevenue.com/en/termos",
    "contact": {
      "name": "TempoRevenue support",
      "email": "contact@temporevenue.com",
      "url": "https://temporevenue.com/developers/"
    }
  },
  "servers": [
    {
      "url": "https://app.temporevenue.com/api/v1",
      "description": "Production"
    }
  ],
  "externalDocs": {
    "description": "Developer documentation, TempoRevenue MCP server and rate limits",
    "url": "https://temporevenue.com/developers/"
  },
  "tags": [
    {
      "name": "Proposals",
      "description": "Create proposal drafts and read their status."
    },
    {
      "name": "Credits",
      "description": "Read the number of proposals you can still create."
    }
  ],
  "security": [
    {
      "ApiKey": []
    }
  ],
  "paths": {
    "/proposals": {
      "post": {
        "operationId": "createProposal",
        "tags": [
          "Proposals"
        ],
        "summary": "Create a proposal draft",
        "description": "Turns a client conversation or a deal description into a proposal draft in your account. If the input states a single price in your currency, it is used as the total; otherwise the price is calculated in code from your saved hourly rate and the estimated hours. Takes 30 to 60 seconds (up to 90). One proposal or credit is debited only after the draft is saved. Counts against the rate limit of 10 requests per minute.",
        "parameters": [
          {
            "name": "Idempotency-Key",
            "in": "header",
            "required": false,
            "description": "1 to 255 printable ASCII characters. Repeating a completed key with the same body returns the same proposal (with the current credits_remaining) and the response header Idempotent-Replay: true, without a new generation or charge. Results are kept for 30 days.",
            "schema": {
              "type": "string",
              "minLength": 1,
              "maxLength": 255,
              "pattern": "^[\\x21-\\x7e]{1,255}$"
            },
            "example": "6f1c2a9e-bloom-store-v1"
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateProposalRequest"
              },
              "example": {
                "input": "Sarah at Bloom & Co needs a new online store in 3 weeks, budget $1,200.",
                "client_name": "Sarah",
                "language": "en"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Draft created.",
            "headers": {
              "Idempotent-Replay": {
                "description": "Present with the value true when this response repeats an earlier request with the same Idempotency-Key.",
                "schema": {
                  "type": "string",
                  "enum": [
                    "true"
                  ]
                }
              }
            },
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Proposal"
                },
                "example": {
                  "id": "5c0e6f3a-0b53-4a0e-9d57-6a4f6f6f2c11",
                  "title": "Online store for Bloom & Co",
                  "editor_url": "https://app.temporevenue.com/proposals/5c0e6f3a-0b53-4a0e-9d57-6a4f6f6f2c11",
                  "status": "draft",
                  "public_url": "https://app.temporevenue.com/p/8Kf2vQ9mT1xLw0aZbN4cRg",
                  "credits_remaining": 12
                }
              }
            }
          },
          "400": {
            "description": "The body is not a JSON object.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "invalid_json",
                    "message": "Request body must be a JSON object."
                  }
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "description": "No proposals or credits left.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "payment_required",
                    "message": "No proposals or credits left. Buy credits or upgrade to continue.",
                    "purchase_url": "https://app.temporevenue.com/usage"
                  }
                }
              }
            }
          },
          "404": {
            "description": "Account profile not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "not_found",
                    "message": "Account profile not found."
                  }
                }
              }
            }
          },
          "409": {
            "description": "A request with this Idempotency-Key is still being processed.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "idempotency_in_progress",
                    "message": "A request with this Idempotency-Key is still being processed. Retry in a few seconds."
                  }
                }
              }
            }
          },
          "422": {
            "description": "Invalid request (invalid_request, profile_incomplete, content_blocked, idempotency_key_reuse, idempotency_key_expired).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "examples": {
                  "invalid_request": {
                    "value": {
                      "error": {
                        "code": "invalid_request",
                        "message": "'input' is required: the client conversation or deal description, as text.",
                        "field": "input"
                      }
                    }
                  },
                  "profile_incomplete": {
                    "value": {
                      "error": {
                        "code": "profile_incomplete",
                        "message": "Finish your TempoRevenue profile (hourly rate, public email and phone) before generating proposals.",
                        "profile_url": "https://app.temporevenue.com/profile"
                      }
                    }
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "502": {
            "description": "Generation failed. Nothing was charged.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "generation_failed",
                    "message": "Proposal generation failed. Nothing was charged; try again."
                  }
                }
              }
            }
          }
        }
      }
    },
    "/proposals/{id}": {
      "get": {
        "operationId": "getProposalStatus",
        "tags": [
          "Proposals"
        ],
        "summary": "Get a proposal's status",
        "description": "Returns the status of a proposal in your account, when the client first opened it and when they accepted it. A proposal that is not in your account returns 404, the same as one that does not exist.",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Proposal id returned by Create a proposal draft.",
            "schema": {
              "type": "string",
              "format": "uuid"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Proposal status.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ProposalStatus"
                },
                "example": {
                  "id": "5c0e6f3a-0b53-4a0e-9d57-6a4f6f6f2c11",
                  "status": "viewed",
                  "viewed_at": "2026-10-01T14:22:09Z",
                  "accepted_at": null,
                  "public_url": "https://app.temporevenue.com/p/8Kf2vQ9mT1xLw0aZbN4cRg"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "404": {
            "description": "Proposal not found.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ErrorResponse"
                },
                "example": {
                  "error": {
                    "code": "not_found",
                    "message": "Proposal not found."
                  }
                }
              }
            }
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    },
    "/credits": {
      "get": {
        "operationId": "getCredits",
        "tags": [
          "Credits"
        ],
        "summary": "Get your credit balance",
        "description": "Returns how many proposals you can still create (the included quota of your plan plus purchased credits) and your plan.",
        "responses": {
          "200": {
            "description": "Credit balance.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Credits"
                },
                "example": {
                  "credits_remaining": 12,
                  "plan": "pro"
                }
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKey": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "tr_live_...",
        "description": "API key created in Integrations in the TempoRevenue app. Send it as Authorization: Bearer tr_live_... Keys are shown once and can be revoked anytime. Up to 5 active keys per account."
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing, malformed or revoked API key.",
        "headers": {
          "WWW-Authenticate": {
            "description": "Bearer realm=\"TempoRevenue\"",
            "schema": {
              "type": "string"
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "error": {
                "code": "invalid_api_key",
                "message": "Missing or invalid API key. Send 'Authorization: Bearer tr_live_...'."
              }
            }
          }
        }
      },
      "RateLimited": {
        "description": "Rate limit exceeded: 10 requests per minute per account.",
        "headers": {
          "Retry-After": {
            "description": "Seconds to wait before retrying.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        },
        "content": {
          "application/json": {
            "schema": {
              "$ref": "#/components/schemas/ErrorResponse"
            },
            "example": {
              "error": {
                "code": "rate_limited",
                "message": "Rate limit exceeded: 10 requests per minute per account.",
                "retry_after_seconds": 23
              }
            }
          }
        }
      }
    },
    "schemas": {
      "CreateProposalRequest": {
        "type": "object",
        "required": [
          "input"
        ],
        "properties": {
          "input": {
            "type": "string",
            "minLength": 1,
            "maxLength": 20000,
            "description": "The client conversation or a description of the deal and the work requested, as plain text."
          },
          "client_name": {
            "type": "string",
            "maxLength": 200,
            "description": "The client's name, if known."
          },
          "language": {
            "type": "string",
            "enum": [
              "en",
              "pt",
              "es",
              "de"
            ],
            "description": "Language of the proposal. If omitted, it follows the language of the input."
          }
        }
      },
      "Proposal": {
        "type": "object",
        "required": [
          "id",
          "title",
          "editor_url",
          "status",
          "public_url",
          "credits_remaining"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "title": {
            "type": [
              "string",
              "null"
            ]
          },
          "editor_url": {
            "type": "string",
            "format": "uri",
            "description": "Open the draft in the TempoRevenue editor to review, edit and share it."
          },
          "status": {
            "type": "string",
            "enum": [
              "draft"
            ]
          },
          "public_url": {
            "type": "string",
            "format": "uri",
            "description": "The link your client reads and accepts at, once you share it."
          },
          "credits_remaining": {
            "type": [
              "integer",
              "null"
            ],
            "description": "Proposals you can still create. null only if the balance could not be read after the proposal was already saved."
          }
        }
      },
      "ProposalStatus": {
        "type": "object",
        "required": [
          "id",
          "status",
          "viewed_at",
          "accepted_at",
          "public_url"
        ],
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid"
          },
          "status": {
            "type": "string",
            "enum": [
              "draft",
              "viewed",
              "accepted"
            ],
            "description": "draft: not opened yet. viewed: the client opened it. accepted: the client accepted it."
          },
          "viewed_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time",
            "description": "When the client first opened the proposal."
          },
          "accepted_at": {
            "type": [
              "string",
              "null"
            ],
            "format": "date-time"
          },
          "public_url": {
            "type": "string",
            "format": "uri"
          }
        }
      },
      "Credits": {
        "type": "object",
        "required": [
          "credits_remaining",
          "plan"
        ],
        "properties": {
          "credits_remaining": {
            "type": "integer",
            "description": "Included quota remaining plus purchased credits."
          },
          "plan": {
            "type": "string",
            "enum": [
              "free",
              "trial",
              "pro"
            ]
          }
        }
      },
      "ErrorResponse": {
        "type": "object",
        "required": [
          "error"
        ],
        "properties": {
          "error": {
            "type": "object",
            "required": [
              "code",
              "message"
            ],
            "additionalProperties": true,
            "properties": {
              "code": {
                "type": "string",
                "enum": [
                  "invalid_json",
                  "invalid_api_key",
                  "payment_required",
                  "no_proposals_left",
                  "not_found",
                  "method_not_allowed",
                  "idempotency_in_progress",
                  "invalid_request",
                  "profile_incomplete",
                  "content_blocked",
                  "idempotency_key_reuse",
                  "idempotency_key_expired",
                  "rate_limited",
                  "generation_failed"
                ]
              },
              "message": {
                "type": "string"
              },
              "field": {
                "type": "string",
                "description": "invalid_request: the field that failed."
              },
              "detail": {
                "type": "string",
                "description": "profile_incomplete, content_blocked: a machine-readable reason."
              },
              "purchase_url": {
                "type": "string",
                "format": "uri",
                "description": "payment_required: where to buy credits or upgrade."
              },
              "profile_url": {
                "type": "string",
                "format": "uri",
                "description": "profile_incomplete: where to finish your profile."
              },
              "retry_after_seconds": {
                "type": "integer",
                "description": "rate_limited: seconds to wait."
              }
            }
          }
        }
      }
    }
  }
}
