> ## Documentation Index
> Fetch the complete documentation index at: https://docs.prov.ae/llms.txt
> Use this file to discover all available pages before exploring further.

# List published projects

> Paginated, filterable list of published projects. Returns the compact shape by default; pass `simplified=false` for the full record.

The UUIDs accepted by `developerIds`, `locationIds`, `statusIds` and `propertyTypes` all come from `GET /public/projects/filters`.



## OpenAPI

````yaml /api-reference/openapi.json get /public/projects
openapi: 3.0.3
info:
  title: Provident — Lead Intake API
  version: 1.4.0
  description: >-
    Server-to-server API for pushing leads into the Provident CRM.


    Core endpoints:

    1. `POST /oauth2/token` — exchange client credentials for a bearer token.

    2. `POST /public/leads` — create a CRM lead.

    3. `PUT /public/leads/{id}` — add details you did not have when you created
    it (quiz answers the customer finished afterwards, a budget, an area). Send
    only what changed; phones and emails are appended, never replaced, and the
    lead's stage, status, owner and funnel are not writable.


    Optional read-only reference data, using the same token: locations,
    developers, projects and project statuses. Use them to send values that
    match ours, or to show project content on your own pages. They share the
    lead endpoint's rate limit, so cache them.


    Authentication is OAuth 2.0 Client Credentials; there is no user login. Note
    that the `/v2` prefix is part of the server URL and is mandatory.


    The intake is deliberately forgiving: name-based lookup fields that cannot
    be matched do NOT fail the request — the lead is still created and the
    unmatched values are returned in `intakeUnresolved`.


    The endpoint is not idempotent at the transport level, but intake hygiene
    runs before the lead is created: a second enquiry from the same phone/email
    within the merge window (~24h) is MERGED into the existing lead and returns
    a different, much shorter 201 body — `{ id, merged: true, matchedOn }` —
    with no contactId, intakeUnresolved, needsIntakeReview or createdAt. Always
    branch on `merged` before reading anything else. Enquiries from
    property-portal sources (Property Finder, Bayut, Dubizzle) are exempt and
    never merge. Retry only on network timeouts, 429 and 5xx.
  contact:
    name: Provident API support
servers:
  - url: https://devapi.prov.ae/v2
    description: Staging / development — build and test here
  - url: https://prodapi.prov.ae/v2
    description: Production — live leads
security: []
tags:
  - name: Authentication
    description: OAuth 2.0 client-credentials token issuance
  - name: Lead intake
    description: Public lead creation
  - name: Reference data
    description: Read-only lookups — locations, developers, projects, project statuses
  - name: Lead follow-up
    description: >-
      Reading back who a lead was assigned to, and recording the agent response
      time
  - name: Webhooks
    description: >-
      Calls Provident makes to YOUR server. Documented here for the payload
      shape; these are not endpoints you call.
paths:
  /public/projects:
    get:
      tags:
        - Reference data
      summary: List published projects
      description: >-
        Paginated, filterable list of published projects. Returns the compact
        shape by default; pass `simplified=false` for the full record.


        The UUIDs accepted by `developerIds`, `locationIds`, `statusIds` and
        `propertyTypes` all come from `GET /public/projects/filters`.
      operationId: listProjects
      parameters:
        - name: q
          in: query
          required: false
          description: Free-text search.
          schema:
            type: string
        - name: developerIds
          in: query
          required: false
          description: 'Comma-separated developer UUIDs. Alias: `developerId`.'
          schema:
            type: string
        - name: locationIds
          in: query
          required: false
          description: 'Comma-separated location UUIDs. Alias: `locationId`.'
          schema:
            type: string
        - name: statusIds
          in: query
          required: false
          description: 'Comma-separated status UUIDs. Alias: `statusId`.'
          schema:
            type: string
        - name: propertyTypes
          in: query
          required: false
          description: 'Comma-separated property-type UUIDs. Alias: `propertyType`.'
          schema:
            type: string
        - name: minPrice
          in: query
          required: false
          schema:
            type: string
        - name: maxPrice
          in: query
          required: false
          schema:
            type: string
        - name: deliveryFrom
          in: query
          required: false
          description: Handover date lower bound.
          schema:
            type: string
        - name: deliveryTo
          in: query
          required: false
          description: Handover date upper bound.
          schema:
            type: string
        - name: ownershipType
          in: query
          required: false
          description: Value from the filters call.
          schema:
            type: string
        - name: campaignType
          in: query
          required: false
          description: Value from the filters call.
          schema:
            type: string
        - name: stockAvailability
          in: query
          required: false
          description: Value from the filters call.
          schema:
            type: string
        - name: simplified
          in: query
          required: false
          description: >-
            `true` (default) returns the compact project shape; `false` returns
            the full record.
          schema:
            type: boolean
            default: true
        - $ref: '#/components/parameters/Page'
        - $ref: '#/components/parameters/Limit'
        - name: offset
          in: query
          required: false
          schema:
            type: integer
        - name: sort
          in: query
          required: false
          schema:
            type: string
        - name: order
          in: query
          required: false
          schema:
            type: string
            enum:
              - asc
              - desc
      responses:
        '200':
          description: Paginated projects
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProjectListResponse'
              example:
                data:
                  - id: 55555555-5555-5555-5555-555555555555
                    title: Marina Heights
                    slug: marina-heights
                    campaignType: Launch
                    propertyTypes:
                      - Apartment
                      - Penthouse
                    startingPrice: '1200000'
                    deliveryDate: '2027-06-30T00:00:00.000Z'
                    brochure: true
                    paymentPlans:
                      - 60/40
                    developer:
                      name: Emaar
                    location:
                      id: 22222222-2222-2222-2222-222222222222
                      name: Dubai Marina
                      fullPath: Dubai | Dubai Marina
                      parentName: Dubai
                    image:
                      url: >-
                        https://media.prov.ae/public/projects/images/marina-heights.jpg
                      sortOrder: 0
                    status:
                      id: 44444444-4444-4444-4444-444444444444
                      name: Off-plan
                meta:
                  page: 1
                  limit: 20
                  total: 84
                  totalPages: 5
        '401':
          $ref: '#/components/responses/Unauthorized'
        '429':
          $ref: '#/components/responses/RateLimited'
      security:
        - bearerAuth: []
components:
  parameters:
    Page:
      name: page
      in: query
      required: false
      description: >-
        1-indexed page number. Projects only — the developers and locations
        endpoints reject it with 422.
      schema:
        type: integer
        minimum: 1
        default: 1
    Limit:
      name: limit
      in: query
      required: false
      description: Page size.
      schema:
        type: integer
        minimum: 1
  schemas:
    ProjectListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/ProjectSimplified'
        meta:
          $ref: '#/components/schemas/PaginationMeta'
    ProjectSimplified:
      type: object
      description: >-
        Compact project shape (the default). Additional fields may be present;
        only these are contractual.
      properties:
        id:
          type: string
          format: uuid
        title:
          type: string
          nullable: true
          example: Marina Heights
        slug:
          type: string
          nullable: true
        campaignType:
          type: string
          nullable: true
        propertyTypes:
          type: array
          items:
            type: string
        startingPrice:
          type: string
          nullable: true
        deliveryDate:
          type: string
          format: date-time
          nullable: true
        brochure:
          type: boolean
        paymentPlans:
          type: array
          items:
            type: string
        developer:
          type: object
          nullable: true
          properties:
            name:
              type: string
              nullable: true
        location:
          $ref: '#/components/schemas/ProjectLocation'
        image:
          $ref: '#/components/schemas/ProjectImage'
        status:
          $ref: '#/components/schemas/ProjectStatus'
      additionalProperties: true
    PaginationMeta:
      type: object
      properties:
        page:
          type: integer
          example: 1
        limit:
          type: integer
          example: 50
        total:
          type: integer
          example: 120
        totalPages:
          type: integer
          example: 3
      description: Page-based paging envelope. Used by the projects endpoints only.
    ApiError:
      type: object
      description: Common error envelope used by every endpoint.
      properties:
        statusCode:
          type: integer
          example: 400
        code:
          type: string
          description: >-
            Stable machine-readable error code — branch on this, not on
            `message`.
          example: BAD_REQUEST
        message:
          type: string
          description: Human-readable description.
        errors:
          type: array
          items:
            type: string
          description: 'Present on 422 only: one entry per field that failed validation.'
        details:
          type: object
          additionalProperties: true
          description: Optional structured extras attached by the failing handler.
        path:
          type: string
          example: /v2/public/leads
        timestamp:
          type: string
          format: date-time
    ProjectLocation:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
          nullable: true
          example: Dubai Marina
        fullPath:
          type: string
          nullable: true
          example: Dubai | Dubai Marina
        parentId:
          type: string
          format: uuid
          nullable: true
        parentName:
          type: string
          nullable: true
          example: Dubai
        level:
          type: string
          nullable: true
      additionalProperties: true
    ProjectImage:
      type: object
      properties:
        url:
          type: string
          nullable: true
        originalFileName:
          type: string
        mimeType:
          type: string
          nullable: true
        sortOrder:
          type: integer
          nullable: true
      additionalProperties: true
    ProjectStatus:
      type: object
      properties:
        id:
          type: string
          format: uuid
        name:
          type: string
          nullable: true
          example: Off-plan
        color:
          type: string
          nullable: true
          example: '#00aaaa'
      additionalProperties: true
  responses:
    Unauthorized:
      description: Missing, invalid, revoked or expired bearer token.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
          example:
            statusCode: 401
            code: OAUTH_TOKEN_EXPIRED
            message: OAuth access token expired
            path: /v2/public/projects/filters
            timestamp: '2026-07-27T06:31:04.512Z'
    RateLimited:
      description: >-
        Request quota for this client exceeded. Wait `Retry-After` seconds, then
        resume.
      headers:
        Retry-After:
          description: Seconds to wait before retrying.
          schema:
            type: integer
        X-RateLimit-Limit:
          $ref: '#/components/headers/XRateLimitLimit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/XRateLimitRemaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/XRateLimitReset'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ApiError'
          example:
            statusCode: 429
            code: RATE_LIMITED
            message: Too Many Requests
            path: /v2/public/leads
            timestamp: '2026-07-27T06:31:04.512Z'
  headers:
    XRateLimitLimit:
      description: Requests allowed in the current window.
      schema:
        type: integer
    XRateLimitRemaining:
      description: Requests remaining in the current window.
      schema:
        type: integer
    XRateLimitReset:
      description: Seconds until the counter resets (end of the UTC day).
      schema:
        type: integer
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: Opaque
      description: >-
        The `access_token` returned by `POST /oauth2/token`, sent as
        `Authorization: Bearer <access_token>`. It is an opaque string, not a
        JWT.

````