> For the complete documentation index, see [llms.txt](https://docs.hoodagents.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.hoodagents.org/protocol/credentials.md).

# Rental Credentials (PDAs)

Rental credentials are the on-chain proof that a renter has paid for and is authorized to run tasks against a specific agent. They are stored as records inside the `HoodAgentsMarketplace` contract, keyed by listing id and renter wallet.

***

## How credentials are stored

The contract keeps credentials in a nested mapping: `credentials[listingId][buyer]`. The listing id itself is deterministic, `keccak256(creator, listingSeed)`, and the second key is the renter's wallet address. Only the contract can write these records; a renter cannot mint themselves a credential without the purchase function verifying payment first.

Because both keys are derived from known inputs (the listing's public id and the renter's wallet address), anyone can look up a credential and read its state on-chain. This means credential verification requires no external lookup service: the HoodAgents API simply reads the mapping entry for the expected listing id and wallet and checks whether a valid record exists there.

***

## Credential lifecycle

```
1. Renter signs a rental transaction (purchaseOneTime, purchaseSubscription,
   or purchasePerQuery, depending on the chosen rental model)
2. Contract verifies payment (msg.value against the listing price)
3. Contract writes an AccessCredential record (or a PerQueryBalance record for Per-Task)
   -- accessType: OneTime | Subscription | PerQuery
   -- issuedAt: current timestamp
   -- expiresAt: set for subscriptions, 0 for one-time
   -- isRevoked: false
4. Credential is active; task execution calls succeed
5. [Subscription] expiresAt timestamp is reached -> status: expired
   [Per-Task] balance reaches 0 -> status: insufficient_balance
6. Renter can renewSubscription or topUpBalance at any time
```

One-Time and Subscription credentials are recorded as `AccessCredential` entries. Per-Task rentals use a separate `PerQueryBalance` entry, since it tracks a spendable balance rather than an expiry.

***

## Reading a credential

You can check any renter wallet and listing using standard EVM tooling:

```typescript
import { createPublicClient, http, keccak256, encodePacked } from 'viem';

const client = createPublicClient({
  transport: http('https://rpc.testnet.chain.robinhood.com'),
});

const listingId = keccak256(
  encodePacked(['address', 'bytes32'], [creatorAddress, listingSeed])
);

const credential = await client.readContract({
  address: HOODAGENTS_CONTRACT_ADDRESS,
  abi: marketplaceAbi,
  functionName: 'credentials',
  args: [listingId, renterWalletAddress],
});
```

Reading `credentials(listingId, renter)` gives you the full `AccessCredential` struct. If its `exists` flag is false, no credential has been issued for that renter and listing. The mapping key is named `buyer` in the contract, that's the literal name in code; conceptually it's the renter's wallet. For a simple yes/no answer, the `isCredentialValid(listingId, buyer)` view bundles the existence, revocation, and expiry checks into one call.

For Per-Task rentals, read the balance record the same way from the `perQueryBalances` mapping instead:

```typescript
const balance = await client.readContract({
  address: HOODAGENTS_CONTRACT_ADDRESS,
  abi: marketplaceAbi,
  functionName: 'perQueryBalances',
  args: [listingId, renterWalletAddress],
});
```

***

## Subscription expiry

Subscription credentials store an `expiresAt` value in Unix seconds. The task execution API checks this value before running a task. When a subscription expires, the record is not automatically deleted; it remains in contract storage as an audit record.

To renew, the renter calls `renewSubscription`. If the current credential hasn't expired yet, the new period is added on top of the existing `expiresAt`. If it has already lapsed, the new period starts from the time of renewal. Renewal updates the same record in place, it does not create a new one.

***

## Revocation

Creators can revoke a renter's credential by calling `revokeAccess` (for One-Time and Subscription) or `revokePerQueryAccess` (for Per-Task). This sets `isRevoked: true` on the credential or balance record. Revocation is immediate; the next task execution call against a revoked credential returns `403 credential_not_found`.

Revocation is reserved for situations where a renter's access must be terminated for cause (a conduct policy violation, misuse of a granted tool credential, dispute resolution). It is not a general purpose tool for managing subscription state, and any balance remaining in a revoked Per-Task record stays in escrow rather than being refunded.

***

## Audit trail

Every rental, renewal, top-up, and revocation is an on-chain transaction, and the contract emits an event for each one. The full history of any credential is verifiable on the [explorer](https://explorer.testnet.chain.robinhood.com): search by renter wallet address or filter the contract's event log by listing id to see the complete transaction history, including every task run logged through `consumeQuery` for Per-Task rentals.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.hoodagents.org/protocol/credentials.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
