> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/voteagora/agora-next/llms.txt
> Use this file to discover all available pages before exploring further.

# Proposals API

> Retrieve governance proposals with filtering, pagination, and detailed metadata

The Proposals API provides access to on-chain and off-chain governance proposals.

## List Proposals

<CodeGroup>
  ```bash GET /api/v1/proposals theme={null}
  curl -H "Authorization: Bearer YOUR_API_KEY" \
    "https://vote.ens.domains/api/v1/proposals?limit=10&filter=relevant"
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    'https://vote.ens.domains/api/v1/proposals?limit=10&filter=relevant',
    {
      headers: {
        'Authorization': 'Bearer YOUR_API_KEY'
      }
    }
  );
  const data = await response.json();
  ```

  ```python Python theme={null}
  import requests

  response = requests.get(
      'https://vote.ens.domains/api/v1/proposals',
      params={'limit': 10, 'filter': 'relevant'},
      headers={'Authorization': 'Bearer YOUR_API_KEY'}
  )
  data = response.json()
  ```
</CodeGroup>

**Location in code:** `src/app/api/v1/proposals/route.ts:46`

### Query Parameters

<ParamField query="limit" type="number" default="10">
  Number of proposals to return (max: 50)
</ParamField>

<ParamField query="offset" type="number" default="0">
  Number of proposals to skip for pagination
</ParamField>

<ParamField query="filter" type="string" default="relevant">
  Filter proposals by status

  * `relevant` - Active and upcoming proposals (excludes cancelled)
  * `everything` - All proposals including cancelled
</ParamField>

<ParamField query="type" type="string" optional>
  Filter by proposal type:

  * `STANDARD` - Standard on-chain proposals
  * `APPROVAL` - Approval voting proposals
  * `OPTIMISTIC` - Optimistic proposals
  * `SNAPSHOT` - Snapshot off-chain proposals
  * `OFFCHAIN_OPTIMISTIC` - Off-chain optimistic proposals
  * `OFFCHAIN_OPTIMISTIC_TIERED` - Tiered off-chain optimistic
  * `OFFCHAIN_STANDARD` - Off-chain standard proposals
  * `OFFCHAIN_APPROVAL` - Off-chain approval voting
  * `OFFCHAIN` - All off-chain proposals
  * `EXCLUDE_ONCHAIN` - Only off-chain proposals
</ParamField>

### Response

<ResponseField name="meta" type="object">
  Pagination metadata

  <Expandable title="properties">
    <ResponseField name="has_next" type="boolean">
      Whether more results are available
    </ResponseField>

    <ResponseField name="next_offset" type="number">
      Offset for the next page
    </ResponseField>

    <ResponseField name="total_returned" type="number">
      Number of proposals in current response
    </ResponseField>

    <ResponseField name="total_count" type="number">
      Total number of proposals matching filter
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="data" type="array">
  Array of proposal objects

  <Expandable title="properties">
    <ResponseField name="id" type="string">
      Unique proposal identifier
    </ResponseField>

    <ResponseField name="proposalId" type="string">
      On-chain proposal ID
    </ResponseField>

    <ResponseField name="title" type="string">
      Proposal title
    </ResponseField>

    <ResponseField name="description" type="string">
      Full proposal description (markdown)
    </ResponseField>

    <ResponseField name="status" type="string">
      Current status: `PENDING`, `ACTIVE`, `SUCCEEDED`, `DEFEATED`, `EXECUTED`, `CANCELLED`
    </ResponseField>

    <ResponseField name="proposalType" type="string">
      Type of proposal (see type parameter above)
    </ResponseField>

    <ResponseField name="startBlock" type="string">
      Block number when voting starts
    </ResponseField>

    <ResponseField name="endBlock" type="string">
      Block number when voting ends
    </ResponseField>

    <ResponseField name="startTime" type="string">
      ISO timestamp when voting starts
    </ResponseField>

    <ResponseField name="endTime" type="string">
      ISO timestamp when voting ends
    </ResponseField>

    <ResponseField name="forVotes" type="string">
      Total votes in favor
    </ResponseField>

    <ResponseField name="againstVotes" type="string">
      Total votes against
    </ResponseField>

    <ResponseField name="abstainVotes" type="string">
      Total abstain votes
    </ResponseField>

    <ResponseField name="quorum" type="string">
      Required quorum for proposal
    </ResponseField>

    <ResponseField name="proposer" type="string">
      Address of proposer
    </ResponseField>

    <ResponseField name="transactionHash" type="string">
      Transaction hash of proposal creation
    </ResponseField>
  </Expandable>
</ResponseField>

### Example Response

```json theme={null}
{
  "meta": {
    "has_next": true,
    "next_offset": 10,
    "total_returned": 10,
    "total_count": 245
  },
  "data": [
    {
      "id": "0x1a2b3c4d5e6f...",
      "proposalId": "123",
      "title": "Upgrade Treasury Timelock",
      "description": "# Proposal Summary\n\nThis proposal upgrades...",
      "status": "ACTIVE",
      "proposalType": "STANDARD",
      "startBlock": "18500000",
      "endBlock": "18550000",
      "startTime": "2024-01-15T10:00:00Z",
      "endTime": "2024-01-18T10:00:00Z",
      "forVotes": "5000000000000000000000",
      "againstVotes": "1000000000000000000000",
      "abstainVotes": "500000000000000000000",
      "quorum": "4000000000000000000000",
      "proposer": "0x1234567890abcdef1234567890abcdef12345678",
      "transactionHash": "0xabcd...1234"
    }
  ]
}
```

## Get Proposal by ID

<CodeGroup>
  ```bash GET /api/v1/proposals/{proposalId} theme={null}
  curl -H "Authorization: Bearer YOUR_API_KEY" \
    "https://vote.ens.domains/api/v1/proposals/123"
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    'https://vote.ens.domains/api/v1/proposals/123',
    {
      headers: {
        'Authorization': 'Bearer YOUR_API_KEY'
      }
    }
  );
  const proposal = await response.json();
  ```
</CodeGroup>

**Location in code:** `src/app/api/v1/proposals/[proposalId]/route.ts:4`

### Path Parameters

<ParamField path="proposalId" type="string" required>
  The unique identifier of the proposal
</ParamField>

### Response

Returns a single proposal object with the same structure as the list endpoint.

### Example Response

```json theme={null}
{
  "id": "0x1a2b3c4d5e6f...",
  "proposalId": "123",
  "title": "Upgrade Treasury Timelock",
  "description": "# Proposal Summary\n\nThis proposal upgrades the treasury timelock contract to add multi-sig support...",
  "status": "ACTIVE",
  "proposalType": "STANDARD",
  "startBlock": "18500000",
  "endBlock": "18550000",
  "forVotes": "5000000000000000000000",
  "againstVotes": "1000000000000000000000",
  "abstainVotes": "500000000000000000000",
  "proposer": "0x1234567890abcdef1234567890abcdef12345678",
  "proposalData": {
    "targets": ["0xTreasuryAddress"],
    "values": ["0"],
    "calldatas": ["0x..."],
    "description": "Upgrade Treasury Timelock"
  }
}
```

## Get Proposal Votes

<CodeGroup>
  ```bash GET /api/v1/proposals/{proposalId}/votes theme={null}
  curl -H "Authorization: Bearer YOUR_API_KEY" \
    "https://vote.ens.domains/api/v1/proposals/123/votes?limit=20&sort=weight"
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch(
    'https://vote.ens.domains/api/v1/proposals/123/votes?limit=20',
    {
      headers: {
        'Authorization': 'Bearer YOUR_API_KEY'
      }
    }
  );
  const votes = await response.json();
  ```
</CodeGroup>

**Location in code:** `src/app/api/v1/proposals/[proposalId]/votes/route.ts:28`

### Path Parameters

<ParamField path="proposalId" type="string" required>
  The unique identifier of the proposal
</ParamField>

### Query Parameters

<ParamField query="limit" type="number" default="20">
  Number of votes to return (max: 1000)
</ParamField>

<ParamField query="offset" type="number" default="0">
  Number of votes to skip for pagination
</ParamField>

<ParamField query="sort" type="string" default="weight">
  Sort order:

  * `weight` - Sort by voting power (highest first)
  * `block_number` - Sort by vote timestamp (newest first)
</ParamField>

### Response

<ResponseField name="meta" type="object">
  Pagination metadata
</ResponseField>

<ResponseField name="data" type="array">
  Array of vote objects

  <Expandable title="properties">
    <ResponseField name="address" type="string">
      Voter's Ethereum address
    </ResponseField>

    <ResponseField name="support" type="number">
      Vote choice: `0` = Against, `1` = For, `2` = Abstain
    </ResponseField>

    <ResponseField name="weight" type="string">
      Voting power used
    </ResponseField>

    <ResponseField name="reason" type="string">
      Optional vote reason/comment
    </ResponseField>

    <ResponseField name="transactionHash" type="string">
      Transaction hash of the vote
    </ResponseField>

    <ResponseField name="blockNumber" type="string">
      Block number when vote was cast
    </ResponseField>
  </Expandable>
</ResponseField>

### Example Response

```json theme={null}
{
  "meta": {
    "has_next": true,
    "next_offset": 20,
    "total_returned": 20,
    "total_count": 156
  },
  "data": [
    {
      "address": "0x1234567890abcdef1234567890abcdef12345678",
      "support": 1,
      "weight": "50000000000000000000000",
      "reason": "This proposal improves security and decentralization.",
      "transactionHash": "0xabc...123",
      "blockNumber": "18525000"
    }
  ]
}
```

## Proposal Types

The API supports multiple proposal types:

### Standard Proposals

Traditional on-chain proposals with for/against/abstain voting.

```typescript theme={null}
{
  proposalType: "STANDARD",
  proposalData: {
    targets: string[],      // Contract addresses to call
    values: string[],       // ETH values to send
    calldatas: string[],    // Encoded function calls
    description: string     // Proposal description
  }
}
```

### Approval Voting Proposals

Voters can approve multiple options from a list.

```typescript theme={null}
{
  proposalType: "APPROVAL",
  proposalData: {
    options: Array<[string[], string[], string[], string]>,
    params: [maxApprovals, criteria, budgetToken, criteriaValue, budget]
  }
}
```

### Optimistic Proposals

Proposals that pass unless explicitly vetoed.

```typescript theme={null}
{
  proposalType: "OPTIMISTIC",
  proposalData: {
    targets: string[],
    values: string[],
    calldatas: string[],
    description: string
  }
}
```

### Snapshot Proposals

Off-chain voting via Snapshot.

```typescript theme={null}
{
  proposalType: "SNAPSHOT",
  proposalData: {
    snapshotId: string,
    choices: string[],
    type: "single-choice" | "approval" | "ranked-choice"
  }
}
```

## Data Sources

Proposals are fetched from multiple sources:

1. **DAO Node API** - Primary source with caching (30 second revalidation)
2. **PostgreSQL Database** - Fallback for when DAO Node is unavailable
3. **Archive Service** - Historical proposal data
4. **Snapshot** - Off-chain voting proposals

## Filtering and Sorting

### Filter by Status

```bash theme={null}
# Only active proposals
GET /api/v1/proposals?filter=relevant

# All proposals including cancelled
GET /api/v1/proposals?filter=everything
```

### Filter by Type

```bash theme={null}
# Only on-chain proposals
GET /api/v1/proposals?type=STANDARD

# Only off-chain proposals
GET /api/v1/proposals?type=EXCLUDE_ONCHAIN

# Snapshot proposals only
GET /api/v1/proposals?type=SNAPSHOT
```

### Pagination Example

```bash theme={null}
# Page 1: First 20 proposals
GET /api/v1/proposals?limit=20&offset=0

# Page 2: Next 20 proposals
GET /api/v1/proposals?limit=20&offset=20

# Page 3: Next 20 proposals
GET /api/v1/proposals?limit=20&offset=40
```

## Error Responses

### Invalid Query Parameters

```json theme={null}
{
  "error": "Invalid query parameters: limit must be between 1 and 50",
  "status": 400
}
```

### Proposal Not Found

```json theme={null}
{
  "error": "Proposal not found",
  "status": 404
}
```

### Authentication Required

```json theme={null}
{
  "error": "Missing or invalid bearer token",
  "status": 401
}
```
