Skip to main content

Developers

Pavr API overview — endpoints, limits, webhooks

Pavr API documentation: bearer-token authentication, endpoints for jobs, invoices and companies, sending in requests, paging, rate limits and webhooks.

What the API does

Pavr's API does two things. It lets your own tools read your jobs, invoices and customer companies, and it lets your own site, phone system or form send in a request, the way work comes in on a call or a web form. Everything else, like changing a job, pricing a proposal or sending an invoice, happens in Pavr, where the approvals and the history live.

The base URL is https://pavr.io/api/v1. Every response is JSON in one envelope, and the sections below cover everything the API has. If an endpoint isn't on this page, it doesn't exist.

Authentication

There are two credentials, and each works on its own endpoints.

  • An API token reads jobs, invoices and companies. Make one under Settings → Connections → API access. An account has one active token at a time, Pavr shows it once, and making a new one there revokes the old one on the spot.
  • An API key sends in requests. Make one under Settings → Connections → API keys and give it the requests:write permission. You can make a key for each tool that posts to you and revoke them one at a time.

Send either one as a bearer token. A token or key belongs to one account and only ever sees that account's work.

curl https://pavr.io/api/v1/jobs \
  -H "Authorization: Bearer $PAVR_API_TOKEN"

A missing, malformed or revoked credential gets a 401. A key without requests:write gets a 403.

Endpoints

Method Path Returns Credential
GET /api/v1/jobs Your jobs, newest first Token
GET /api/v1/jobs/:id One job Token
GET /api/v1/invoices Your invoices, newest first Token
GET /api/v1/invoices/:id One invoice Token
GET /api/v1/companies Your customer companies, newest first Token
GET /api/v1/companies/:id One company Token
POST /api/v1/requests Sends in a request Key with requests:write

There's nothing to update or delete over the API, and no endpoints for proposals, change orders or work orders. Sample jobs and the invoices on them stay inside Pavr, so they never show up here. An id from another account, or one that doesn't exist, gets the same 404.

The response envelope

Every body has the same three keys.

{
  "data": { "id": "…", "name": "…" },
  "meta": { "request_id": "…", "api_version": "v1" },
  "errors": null
}

data is one object, or a list on an index endpoint. meta always carries a request_id to quote if you write to us, and api_version. When something goes wrong, data is null and errors is a list of { "code", "message" }. The codes you can see are unauthorized, insufficient_scope, not_found, bad_request, validation_failed and rate_limited.

Jobs

Each job carries the fields below. Dates and times are ISO 8601, and money is in cents.

Field What it is
id, job_number The job's id, and the number your team says on the phone
name The job's name
billing_type fixed_bid, progress or time_and_materials
display_status, display_status_label The one word every Pavr screen shows for the job, and its label. For an open job the word is open and the label is the name of the sales stage; after that it's won, in_operations, ready_to_invoice, invoiced, paid, lost or closed
sales_status open, won, lost or closed
sales_stage The name of the stage on your Jobs board
operations_status not_started, in_operations or ready_to_invoice
lifecycle_state active. Archived jobs aren't returned
signed_at, won_at, lost_at, closed_at When each happened, or null
expected_completion_date The date on the job, or null
geofence_posture, geofence_radius_m_override soft or strict: the job's clock-in location setting. The radius override is in meters, or null
company_id, location_id, request_id, sales_stage_id, recipient_company_id, recipient_contact_id The ids to look up what the job belongs to. request_id is the request it came from
assigned_pm_user_id, assigned_foreman_user_id Who the job is assigned to
account_id, created_at, updated_at Your account, and when the job was made and last changed

Invoices

Field What it is
id, invoice_number, job_id The invoice's id and number, and the job it bills
invoice_type fixed_bid, progress_milestone, progress_period, retainage_release, tm_weekly, deposit or late_fee
status draft, finalized, sent, partial_paid, paid, past_due or voided
subtotal_cents, tax_cents, total_cents, balance_due_cents The money on the invoice, and what's still owed
finalized_at, due_at, paid_at When each happened, or null
created_at, updated_at When the invoice was made and last changed

Companies

Field What it is
id, name The company's id and name
billing_method_preference, tax_exempt How it likes to be billed, and whether it's tax exempt
website, notes What you've saved on the company
lifecycle_state, on_hold active (archived companies aren't returned), and whether the company has a hold on it
created_at, updated_at When the company was made and last changed

Creating a request

curl -X POST https://pavr.io/api/v1/requests \
  -H "Authorization: Bearer $PAVR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 3f7a9e2c-1b4d-4e6a-9c3f-8a2d5e7f1b90" \
  -d '{"request": {"name": "Dana Ruiz", "email": "dana@example.com", "phone": "555-0143", "address": "18000 Parking Lot Way"}}'

Put the fields inside a request object. Pavr takes name, email, phone, company_name, address, proposal_due_at, walk_at and a free-form payload object. Send at least an email or a phone, so your team can get back to them. Without one you get a 422 that says so.

The request lands in your Requests list with the status new, and the response returns the fields you sent with its id, status and created_at. Someone on your team looks at it and makes it a job or declines it.

Idempotency-Key is optional, and it's how you retry safely. Send the same key again and you get the same request back with a 200, not a second one. The first call returns a 201.

Paging

Index endpoints return 25 items a page, newest first. Ask for another page with page.

GET /api/v1/jobs?page=2

The page size is fixed, and a per_page or limit parameter does nothing. meta tells you where you are:

"meta": { "request_id": "…", "api_version": "v1", "page": 2, "per_page": 25, "next_page": 3 }

next_page is null on the last page. There's no total count, so keep asking until next_page is null.

Rate limits

60 requests per minute, per account, counted in a fixed window that resets at the top of each minute. Reads and request intake share the same count.

Go over and you get a 429 in the envelope with a Retry-After header, in seconds, until the window resets. It's the only rate-limit header the API sends, so don't build against X-RateLimit-*.

HTTP/1.1 429 Too Many Requests
Retry-After: 37

Sending in requests has two more limits, checked before the API sees the request: 60 a minute for each API key and 100 a minute for each IP address. Go over either and you get a 429 with the plain text Retry later and no Retry-After header, so wait a minute before you try again.

Webhooks

Pavr can post to your address when a job is opened, a job is won, a payment lands on an invoice, or a proposal is signed. The Webhook reference has the events, the payloads, how to check the signature and what happens when your server is down.