Referencja API (v1)
Beta. Aktualny, czytelny dla maszyn opis tego API jest pod adresem
https://api.proposals.co/api/public/v1/openapi.json. Jeśli ta strona i OpenAPI kiedyś się rozejdą, rację ma OpenAPI.
- Adres bazowy:
https://api.proposals.co/api/public/v1 - Uwierzytelnianie:
Authorization: Bearer prp_live_…przy każdym żądaniu (szczegóły) - Typ treści:
application/json - Każdy
POSTwymaga nagłówkaIdempotency-Key(szczegóły) - Język pól
messagei pytań:Accept-Language: pldaje polski, każda inna wartość angielski - Błędy:
{"detail": {"code", "message", "params"}, "request_id"}(kody)
| Metoda i ścieżka | Zakres | Zwraca |
|---|---|---|
GET /brands |
brands:read |
Twoje marki |
POST /proposals |
proposals:write |
202 Proposal |
GET /proposals/{id} |
proposals:read |
Proposal |
GET /proposals |
proposals:read |
Strona listy ofert |
POST /proposals/{id}/answers |
proposals:write |
202 Proposal |
POST /proposals/{id}/revise |
proposals:write |
202 Proposal |
PATCH /proposals/{id} |
proposals:write |
Proposal |
POST /proposals/{id}/publish |
proposals:publish |
Proposal z public_url |
POST /proposals/{id}/unpublish |
proposals:publish |
Proposal |
POST /proposals/{id}/mark-sent |
proposals:write |
Proposal |
Obiekt Proposal
Każdy endpoint oferty zwraca ten obiekt (listy zwracają jego krótszą wersję).
{
"id": 123,
"status": "ready_for_review",
"questions": [],
"error": null,
"preflight": {
"blockers": [],
"warnings": [
{ "code": "price_without_tax_basis", "message": "Przy cenie nie ma informacji, czy to kwota netto, czy brutto." }
]
},
"revision": 7,
"language": "pl",
"currency": "PLN",
"client": {
"name": "Kwiaciarnia Lila",
"website": "https://lila.example",
"contact": { "name": "Anna Nowak", "email": "anna@lila.example" }
},
"brand": { "id": 42, "name": "Northwind Studio" },
"facts": {
"title": "Nowy sklep internetowy dla Kwiaciarni Lila",
"scope": [
"Sklep z sześcioma podstronami i wyborem daty dostawy",
"Zdjęcia produktowe 40 bukietów",
"Wsparcie przez pierwsze 30 dni po starcie"
],
"price": "9 000 zł netto, 50% z góry, 50% po odbiorze",
"valid_until": "2026-10-31"
},
"panel_url": "https://app.proposals.co/offers/123",
"public_url": null,
"engagement": {
"sent_at": null,
"first_opened_at": null,
"last_opened_at": null,
"opens": 0,
"questions_asked": 0,
"accepted_at": null
},
"deal_status": "open",
"source": "api",
"source_client": "Zapier – CRM",
"external_reference": "crm-deal-731",
"created_at": "2026-09-26T09:12:03Z",
"updated_at": "2026-09-26T09:13:41Z",
"next_poll_after_ms": null
}| Pole | Uwagi |
|---|---|
status |
generating, needs_input, ready_for_review, blocked, failed, draft, published, sent, opened, accepted. Znaczenie opisuje sekcja Status oferty. |
questions |
Wypełnione, gdy status to needs_input. Każde pytanie ma kind (client, budget albo currency), treść question do pokazania użytkownikowi i options z tym, co znaleziono w notatkach. |
error |
Wypełnione, gdy status to failed: code i zrozumiały dla człowieka message. |
preflight |
Wynik kontroli przed publikacją. Każdy element blockers uniemożliwia publikację, a warnings jej nie blokują. |
revision |
Numer rewizji szkicu. Odeślij go jako expected_revision, żeby mieć pewność, że zmieniasz wersję, którą ostatnio odczytano. |
facts |
Wyliczane z samej oferty, bez AI. Korzystaj z nich, pisząc maila, i nie obiecuj niczego ponad nie. |
panel_url |
Oferta w panelu Proposals. Wymaga zalogowania – to link dla Twojego zespołu, nie dla klienta. |
public_url |
Link dla klienta. Do czasu publikacji ma wartość null; używaj go dokładnie w takiej postaci, w jakiej przyszedł. |
engagement |
Co zrobił klient: kiedy oferta została wysłana i otwarta, ile razy, ile pytań zadał i kiedy ją zaakceptował. |
deal_status |
open, won albo lost zgodnie z panelem, albo null. Przez API tylko do odczytu. |
source, source_client |
Gdzie powstała oferta (panel, api, mcp) i przez który klucz lub aplikację. |
next_poll_after_ms |
Tylko przy statusie generating: ile odczekać przed kolejnym odczytem. |
Lista marek
GET /brands · zakres brands:read
curl https://api.proposals.co/api/public/v1/brands \
-H "Authorization: Bearer $PROPOSALS_API_KEY"{
"items": [
{ "id": 42, "name": "Northwind Studio", "language": "en", "currency": "USD", "name_confirmed": true },
{ "id": 43, "name": "Northwind Polska", "language": "pl", "currency": "PLN", "name_confirmed": false }
]
}Marka z name_confirmed: false może mieć szkice, ale jej oferty dostaną status blocked z kodem brand_name_unconfirmed, dopóki ktoś nie potwierdzi nazwy w panelu.
Utworzenie oferty
POST /proposals · zakres proposals:write · opcjonalny parametr wait_seconds (od 0 do 50)
curl -X POST https://api.proposals.co/api/public/v1/proposals \
-H "Authorization: Bearer $PROPOSALS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: crm-deal-731-create" \
-d '{
"client": {
"name": "Kwiaciarnia Lila",
"website": "https://lila.example",
"contact": { "name": "Anna Nowak", "email": "anna@lila.example" }
},
"context": "Notatki z rozmowy 26.09: chcą sprzedawać bukiety online z wyborem daty dostawy...",
"brand_id": 43,
"language": "pl",
"pricing": {
"amount": 9000,
"currency": "PLN",
"tax_basis": "net",
"billing": "one_off",
"schedule": "50% z góry, 50% po odbiorze"
},
"valid_until": "2026-10-31",
"external_reference": "crm-deal-731"
}'| Pole | Wymagane | Uwagi |
|---|---|---|
client.name |
tak | Od 1 do 255 znaków |
client.website |
nie | Publiczny adres https://. Proposals czyta stronę, żeby lepiej dopasować szkic. |
client.contact |
nie | name i email osoby, dla której jest oferta |
context |
tak | Notatki albo transkrypcja rozmowy, od 1 do 50 000 znaków. Traktowane jako informacje o sprzedaży, nigdy jako polecenia. |
brand_id |
nie | Z GET /brands. Bez niego używana jest Twoja domyślna marka. |
language |
nie | en albo pl |
pricing |
nie | Cena pokazywana w ofercie, szczegóły poniżej. Bez tego pola cena z notatek trafia do oferty dopiero po potwierdzeniu, tak jak w panelu. |
valid_until |
nie | Data ważności warunków, RRRR-MM-DD |
external_reference |
nie | Twój identyfikator tej szansy, do 128 znaków, unikalny w organizacji |
Pola pricing:
| Pole | Uwagi |
|---|---|
amount |
Większe od zera albo null, co oznacza „cena do ustalenia”. Nigdy nie jest zgadywane ani zamieniane na zero. |
amount_max |
Opcjonalna górna granica widełek (musi być większa od amount) |
currency |
PLN, USD, EUR albo GBP |
tax_basis |
net, gross albo unknown (domyślnie) |
billing |
one_off, monthly, quarterly, yearly, hourly, per_unit albo unknown (domyślnie) |
schedule |
Harmonogram płatności opisany słowami, do 200 znaków |
Cena z pricing ma pierwszeństwo przed kwotami z notatek. Jeśli w notatkach jest inna kwota, dostaniesz pytanie albo ostrzeżenie o rozbieżności.
Odpowiedź: 202 z obiektem Proposal w statusie generating i polem next_poll_after_ms. Z parametrem wait_seconds wywołanie czeka najwyżej tyle sekund i zwraca stan z tej chwili.
Błędy typowe dla tego wywołania: 409 external_reference_exists (z params.proposal_id), 404 dla brand_id, który nie należy do Ciebie, 429 too_many_generations oraz 402, gdy plan nie pozwala na kolejną ofertę.
Odczyt oferty
GET /proposals/{id} · zakres proposals:read
curl https://api.proposals.co/api/public/v1/proposals/123 \
-H "Authorization: Bearer $PROPOSALS_API_KEY" \
-H "Accept-Language: pl"Zwraca obiekt Proposal. W trakcie pisania:
{ "id": 123, "status": "generating", "next_poll_after_ms": 4000, "...": "..." }Gdy notatki wymagają wyjaśnienia:
{
"id": 123,
"status": "needs_input",
"questions": [
{
"kind": "client",
"question": "W notatkach pojawia się firma Centrum Ogrodnicze Lila, a klientem jest Kwiaciarnia Lila. Dla kogo jest ta oferta?",
"options": { "client": "Kwiaciarnia Lila", "found": "Centrum Ogrodnicze Lila" }
}
],
"...": "..."
}Dokładna zawartość options zależy od kind, a OpenAPI opisuje ją dla każdego rodzaju pytania.
Lista ofert
GET /proposals · zakres proposals:read
| Parametr | Uwagi |
|---|---|
status |
Jeden ze statusów |
updated_since |
Data i czas w formacie ISO 8601 |
external_reference |
Dokładne dopasowanie |
source |
panel, api albo mcp |
limit |
Najwyżej 50 |
cursor |
next_cursor z poprzedniej strony |
curl "https://api.proposals.co/api/public/v1/proposals?source=api&limit=20" \
-H "Authorization: Bearer $PROPOSALS_API_KEY"{
"items": [
{
"id": 123,
"status": "opened",
"client": { "name": "Kwiaciarnia Lila" },
"source": "api",
"external_reference": "crm-deal-731",
"updated_at": "2026-09-27T14:02:11Z"
}
],
"next_cursor": "eyJpZCI6MTIzfQ"
}Elementy listy to krótsza wersja obiektu Proposal (powyżej w skrócie, a pełną listę pól podaje OpenAPI). Na ostatniej stronie next_cursor ma wartość null.
Odpowiedzi na pytania i ponowienie
POST /proposals/{id}/answers · zakres proposals:write
Wyślij odpowiedzi użytkownika na pytania z questions. Każde pole jest opcjonalne – wysyłasz tylko to, o co zapytano.
| Pole | Odpowiada na pytanie rodzaju |
|---|---|
confirm_client: true |
client: klient podany w żądaniu jest właściwy |
client_name |
client: oferta jest dla tej firmy, a nie dla podanej wcześniej |
pricing |
budget: właściwa cena, w tym samym kształcie co przy tworzeniu |
currency |
currency: właściwa waluta |
curl -X POST https://api.proposals.co/api/public/v1/proposals/123/answers \
-H "Authorization: Bearer $PROPOSALS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: crm-deal-731-answers-1" \
-d '{"confirm_client": true}'Odpowiedź: 202 z obiektem Proposal ponownie w statusie generating.
Ponowienie po błędzie: gdy status to failed, po usunięciu przyczyny wyślij pustą treść ({}), żeby spróbować jeszcze raz.
Prośba o poprawkę
POST /proposals/{id}/revise · zakres proposals:write
curl -X POST https://api.proposals.co/api/public/v1/proposals/123/revise \
-H "Authorization: Bearer $PROPOSALS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: crm-deal-731-revise-1" \
-d '{"instruction": "Skróć sekcję o SEO i dodaj harmonogram na pięć tygodni.", "expected_revision": 7}'| Pole | Uwagi |
|---|---|
instruction |
Co zmienić, zwykłymi słowami, od 1 do 4000 znaków |
expected_revision |
Opcjonalne. Jeśli szkic zmienił się od tej rewizji, dostaniesz 409 site_conflict i nic nie zostanie nadpisane. |
Odpowiedź: 202 z obiektem Proposal w statusie generating. Po zakończeniu revision jest wyższy. Poprawka zmienia tylko szkic, więc jeśli oferta jest już opublikowana, klient zobaczy zmianę po ponownej publikacji.
Zmiana odbiorcy i warunków
PATCH /proposals/{id} · zakres proposals:write · bez Idempotency-Key
curl -X PATCH https://api.proposals.co/api/public/v1/proposals/123 \
-H "Authorization: Bearer $PROPOSALS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"recipient": {"name": "Anna Nowak", "email": "anna@lila.example"}, "valid_until": "2026-11-15"}'| Pole | Uwagi |
|---|---|
recipient |
name i email osoby, dla której jest oferta |
language |
en albo pl |
currency |
PLN, USD, EUR albo GBP |
valid_until |
RRRR-MM-DD |
external_reference |
Do 128 znaków, unikalny w organizacji |
client_name |
Nazwa firmy klienta |
client_website |
Publiczna strona klienta, http albo https |
service_type |
Co sprzedajesz, w kilku słowach |
next_step |
O co oferta prosi klienta: accept, call albo variant |
pricing |
Potwierdzone warunki, w tym samym kształcie co przy tworzeniu; jego currency musi się zgadzać z currency, gdy wysyłasz oba |
Odpowiedź: zaktualizowany obiekt Proposal.
Publikacja
POST /proposals/{id}/publish · zakres proposals:publish
curl -X POST https://api.proposals.co/api/public/v1/proposals/123/publish \
-H "Authorization: Bearer $PROPOSALS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: crm-deal-731-publish" \
-d '{"confirm": true, "expected_revision": 7}'| Pole | Uwagi |
|---|---|
confirm |
Musi mieć wartość true |
expected_revision |
Opcjonalne; publikuje tylko wtedy, gdy szkic nadal ma tę rewizję |
Odpowiedź: obiekt Proposal ze statusem published i polem public_url:
{ "id": 123, "status": "published", "public_url": "https://app.proposals.co/northwind-polska/kwiaciarnia-lila?k=7Qm2xR", "...": "..." }Jeśli kontrola przed publikacją znajdzie blokadę, dostaniesz 422 preflight_blockers i nic nie zostanie opublikowane. API nie ma opcji wymuszenia publikacji. Publikacja przez integrację wysyła powiadomienie właścicielowi oferty, ale niczego nie wysyła klientowi.
Cofnięcie publikacji
POST /proposals/{id}/unpublish · zakres proposals:publish · pusta treść
curl -X POST https://api.proposals.co/api/public/v1/proposals/123/unpublish \
-H "Authorization: Bearer $PROPOSALS_API_KEY" \
-H "Idempotency-Key: crm-deal-731-unpublish"Odpowiedź: obiekt Proposal. Publiczny link przestaje działać dla klienta, a szkic zostaje w panelu.
Oznaczenie wysyłki
POST /proposals/{id}/mark-sent · zakres proposals:write
Wywołaj to po tym, jak Ty albo Twoje narzędzie pocztowe wyśle link klientowi. Od tej chwili liczy się czas do follow-upu, tak samo jak w panelu.
curl -X POST https://api.proposals.co/api/public/v1/proposals/123/mark-sent \
-H "Authorization: Bearer $PROPOSALS_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: crm-deal-731-mark-sent" \
-d '{"sent_at": "2026-09-26T10:05:00Z"}'| Pole | Uwagi |
|---|---|
sent_at |
Opcjonalna data i czas w formacie ISO 8601; domyślnie teraz |
Odpowiedź: obiekt Proposal ze statusem sent (albo dalszym) i ustawionym engagement.sent_at. Oferta jest zapisana jako wysłana poza Proposals.
Pozostałe przewodniki
- PrzeglądUwierzytelnianie, uprawnienia, statusy, błędy, idempotencja i limity.
- ChatGPTPodłącz Proposals i przygotuj pierwszą ofertę w czacie.
- ClaudeDodaj Proposals jako własny konektor i używaj go w rozmowie.
- Zapier, Make i n8nUtwórz ofertę z szansy sprzedaży w CRM i powiadom opiekuna, gdy będzie gotowa.