Skip to content

Quotation API

Quote and work package management endpoints.

Status

These endpoints are planned and not yet implemented.

Endpoints

List Quotes

Returns a paginated list of quotes.

http
GET /api/quotation/quotes

Query Parameters

ParameterTypeDefaultDescription
pageinteger1Page number
pageSizeinteger10Items per page
customerIdguid-Filter by customer
statusstring-Filter by status (draft, sent, accepted, rejected)

Success Response 200 OK

json
{
  "data": [
    {
      "id": "550e8400-e29b-41d4-a716-446655440000",
      "number": "Q-2026-0001",
      "customerId": "660e8400-e29b-41d4-a716-446655440000",
      "customerName": "John Doe",
      "title": "Bathroom Renovation",
      "totalAmount": 125000.00,
      "currency": "SEK",
      "status": "draft",
      "validUntil": "2026-02-12T00:00:00Z",
      "createdAt": "2026-01-12T10:00:00Z"
    }
  ],
  "meta": {
    "total": 1,
    "page": 1,
    "pageSize": 10
  }
}

Get Quote

Returns a single quote with all work packages.

http
GET /api/quotation/quotes/{id}

Success Response 200 OK

json
{
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "number": "Q-2026-0001",
    "customerId": "660e8400-e29b-41d4-a716-446655440000",
    "customerName": "John Doe",
    "title": "Bathroom Renovation",
    "description": "Complete bathroom renovation including tiles, fixtures, and plumbing",
    "workPackages": [
      {
        "id": "770e8400-e29b-41d4-a716-446655440000",
        "name": "Demolition",
        "description": "Remove existing fixtures and tiles",
        "items": [
          {
            "id": "880e8400-e29b-41d4-a716-446655440000",
            "articleNumber": "LABOR-001",
            "description": "Demolition labor",
            "quantity": 16,
            "unit": "hours",
            "unitPrice": 650.00,
            "totalPrice": 10400.00
          }
        ],
        "subtotal": 10400.00
      },
      {
        "id": "990e8400-e29b-41d4-a716-446655440000",
        "name": "Tiling",
        "description": "Install new floor and wall tiles",
        "items": [
          {
            "id": "aa0e8400-e29b-41d4-a716-446655440000",
            "articleNumber": "TILE-FLOOR-001",
            "description": "Floor tiles 30x30",
            "quantity": 15,
            "unit": "sqm",
            "unitPrice": 450.00,
            "totalPrice": 6750.00
          },
          {
            "id": "bb0e8400-e29b-41d4-a716-446655440000",
            "articleNumber": "LABOR-002",
            "description": "Tiling labor",
            "quantity": 24,
            "unit": "hours",
            "unitPrice": 650.00,
            "totalPrice": 15600.00
          }
        ],
        "subtotal": 22350.00
      }
    ],
    "subtotal": 32750.00,
    "vatRate": 25,
    "vatAmount": 8187.50,
    "totalAmount": 40937.50,
    "currency": "SEK",
    "status": "draft",
    "validUntil": "2026-02-12T00:00:00Z",
    "createdAt": "2026-01-12T10:00:00Z",
    "updatedAt": "2026-01-12T14:30:00Z"
  },
  "error": null,
  "meta": {
    "timestamp": "2026-01-12T10:00:00Z",
    "requestId": "abc-123"
  }
}

Create Quote

Creates a new quote.

http
POST /api/quotation/quotes

Request Body

json
{
  "customerId": "660e8400-e29b-41d4-a716-446655440000",
  "title": "Bathroom Renovation",
  "description": "Complete bathroom renovation",
  "validUntil": "2026-02-12T00:00:00Z"
}

Success Response 201 Created

json
{
  "data": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "number": "Q-2026-0001"
  },
  "error": null,
  "meta": {
    "timestamp": "2026-01-12T10:00:00Z",
    "requestId": "def-456"
  }
}

Add Work Package

Adds a work package to a quote.

http
POST /api/quotation/quotes/{quoteId}/work-packages

Request Body

json
{
  "name": "Demolition",
  "description": "Remove existing fixtures and tiles"
}

Success Response 201 Created

json
{
  "data": {
    "id": "770e8400-e29b-41d4-a716-446655440000"
  },
  "error": null,
  "meta": {
    "timestamp": "2026-01-12T10:00:00Z",
    "requestId": "ghi-789"
  }
}

Add Line Item

Adds a line item to a work package.

http
POST /api/quotation/quotes/{quoteId}/work-packages/{workPackageId}/items

Request Body

json
{
  "articleNumber": "LABOR-001",
  "description": "Demolition labor",
  "quantity": 16,
  "unit": "hours",
  "unitPrice": 650.00
}

Success Response 201 Created

json
{
  "data": {
    "id": "880e8400-e29b-41d4-a716-446655440000",
    "totalPrice": 10400.00
  },
  "error": null,
  "meta": {
    "timestamp": "2026-01-12T10:00:00Z",
    "requestId": "jkl-012"
  }
}

Send Quote

Sends a quote to the customer.

http
POST /api/quotation/quotes/{id}/send

Request Body

json
{
  "recipientEmail": "customer@example.com",
  "message": "Please find attached our quote for your bathroom renovation."
}

Success Response 200 OK

json
{
  "data": {
    "status": "sent",
    "sentAt": "2026-01-12T15:00:00Z"
  },
  "error": null,
  "meta": {
    "timestamp": "2026-01-12T15:00:00Z",
    "requestId": "mno-345"
  }
}

Quote Status Flow

┌─────────┐     ┌────────┐     ┌──────────┐
│  Draft  │ ──► │  Sent  │ ──► │ Accepted │ ──► Convert to Contract
└─────────┘     └────────┘     └──────────┘


               ┌──────────┐
               │ Rejected │
               └──────────┘

Frontend Integration

typescript
// features/quotation/hooks/useQuotes.ts
export function useQuotes(params?: QuoteQueryParams) {
  return useQuery({
    queryKey: ['quotes', params],
    queryFn: () => quotationApi.getQuotes(params),
  });
}

export function useQuote(id: string) {
  return useQuery({
    queryKey: ['quotes', id],
    queryFn: () => quotationApi.getQuote(id),
  });
}

export function useCreateQuote() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (data: CreateQuoteRequest) => quotationApi.createQuote(data),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: ['quotes'] });
    },
  });
}

export function useSendQuote() {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: ({ id, data }: { id: string; data: SendQuoteRequest }) =>
      quotationApi.sendQuote(id, data),
    onSuccess: (_, { id }) => {
      queryClient.invalidateQueries({ queryKey: ['quotes', id] });
    },
  });
}

Built with VitePress | v1.1.0