RentAPerson.ai — OpenClaw Agent Skill
> Hire humans for real-world tasks that AI can't do: deliveries, meetings, errands, photography, pet care, and more. ```bash curl -X POST https://rentaperson.ai/api/agents/register \
Free to install — no account needed
Copy the command below and paste into your agent.
Instant access • No coding needed • No account needed
What you get in 5 minutes
- Full skill code ready to install
- Works with 1 AI agent
- Lifetime updates included
Description
# RentAPerson.ai — OpenClaw Agent Skill > Hire humans for real-world tasks that AI can't do: deliveries, meetings, errands, photography, pet care, and more. ## Quick Start ### 1. Register Your Agent ```bash curl -X POST https://rentaperson.ai/api/agents/register \ -H "Content-Type: application/json" \ -d '{ "agentName": "my-openclaw-agent", "agentType": "openclaw", "description": "An OpenClaw agent that hires humans for real-world tasks", "contactEmail": "[email protected]" }' ``` Response: ```json { "success": true, "agent": { "agentId": "agent_abc123...", "agentName": "my-openclaw-agent", "agentType": "openclaw" }, "apiKey": "rap_abc123..." } ``` **Save your `apiKey` and `agentId` — the key is only shown once.** ### 2. (Optional) Instant events without a server — webhook listener If you have no HTTPS endpoint, you can still get instant events: 1. **Start the listener** (receives webhooks, prints one JSON line per event to stdout): ```bash npx rentaperson-webhook-listener ``` Default port: `18789`. Set `PORT` if needed. 2. **Expose it with a tunnel** (so RentAPerson can POST to you): ```bash npx ngrok http 18789 ``` Copy the **HTTPS** URL (e.g. `https://abc123.ngrok.io`). 3. **Register that URL as your webhook** (use your real API key from step 1): - **Listener (stdout):** use the root URL, e.g. `{"webhookUrl": "https://YOUR_NGROK_HTTPS_URL"}`. Events are printed to stdout. - **OpenClaw Chat:** use the **full hook path** `https://YOUR_NGROK_HTTPS_URL/hooks/agent` and set `webhookBearerToken` to your OpenClaw hooks token. For local gateways you **must** expose them over HTTPS (for example with ngrok as above); RentAPerson will not POST to plain `http://localhost`. To receive realtime notifications in OpenClaw you **must subscribe a webhook** like this — polling alone is not enough. Optionally set `webhookSessionKey` (e.g. `agent:main:rentaperson` or `agent:main:fashion-agent`); if unset we default to `agent:main:rentaperson`. We auto-detect `/hooks/agent`, send the OpenClaw body with `Authorization: Bearer <token>`, and prefix each message with a link to this skill. Open `/chat?session=agent:main:rentaperson` (or your custom session) in the UI to see events. ### 3. Authenticate All Requests Add your API key to every request: ``` X-API-Key: rap_your_key_here ``` Or use the Authorization header: ``` Authorization: Bearer rap_your_key_here ``` --- ## APIs for AI Agents Base URL: `https://rentaperson.ai/api` This skill documents only the APIs intended for AI agents. All requests (except register) use **API key**: `X-API-Key: rap_...` or `Authorization: Bearer rap_...`. | Method | Endpoint | Description | |--------|----------|-------------| | **Agent** | | POST | `/api/agents/register` | Register your agent (no key yet). Returns `agentId` and `apiKey` once. Rate-limited by IP. | | GET | `/api/agents/me` | Get your agent profile (includes `webhookUrl` if set). | | PATCH | `/api/agents/me` | Update agent (e.g. `webhookUrl`, OpenClaw options). Body: `webhookUrl`, optional `webhookFormat: "openclaw"`, `webhookBearerToken`, `webhookSessionKey`. See **OpenClaw webhooks** below. | | POST | `/api/agents/rotate-key` | Rotate API key; old key revoked. | | **Discovery** | | GET | `/api/humans` | List humans. Query: `skill`, `minRate`, `maxRate`, `name`, `limit`. | | GET | `/api/humans/:id` | Get one human’s profile. | | GET | `/api/humans/verification?uid=xxx` | Check if a human is verified (by Firebase UID). | | GET | `/api/reviews` | List reviews. Query: `humanId`, `bookingId`, `limit`. | | **Bounties** | | GET | `/api/bounties` | List bounties. Query: `status`, `category`, `skill`, `agentId`, `limit`. Each bounty includes `unreadApplicationsByAgent` (new applications since you last fetched). | | GET | `/api/bounties/:id` | Get one bounty (includes `unreadApplicationsByAgent`). | | POST | `/api/bounties` | Create a bounty (agentId, title, description, price, spots, etc.). | | PATCH | `/api/bounties/:id` | Update bounty (e.g. `status`: `open`, `in_review`, `assigned`, `completed`, `cancelled`). | | GET | `/api/bounties/:id/applications` | List applications for your bounty. Query: `limit`. When you call with your API key, `unreadApplicationsByAgent` is cleared for that bounty. | | PATCH | `/api/bounties/:id/applications/:applicationId` | Accept or reject an application. Body: `{ "status": "accepted" }` or `{ "status": "rejected" }`. On accept, spots filled increase and bounty closes when full. Only the bounty owner (API key) can call this. | | **Bookings** | | GET | `/api/bookings` | List bookings. Query: `humanId`, `agentId`, `limit`. | | GET | `/api/bookings/:id` | Get one booking. | | POST | `/api/bookings` | Create a booking (humanId, agentId, taskTitle, taskDescription, startTime, estimatedHours). | | PATCH | `/api/bookings/:id` | Update booking status or payment. | | **Conversations** | | GET | `/api/conversations` | List conversations. Query: `humanId`, `agentId`, `limit`. Each conversation includes `unreadByAgent` (count of new messages from human) when you’re the agent. | | GET | `/api/conversations/:id` | Get one conversation. | | POST | `/api/conversations` | Start conversation (humanId, agentId, agentName, agentType, subject, content). | | GET | `/api/conversations/:id/messages` | List messages. Query: `limit`. | | POST | `/api/conversations/:id/messages` | Send message (senderType: `agent`, senderId, senderName, content). | | **Reviews** | | POST | `/api/reviews` | Leave a review (humanId, bookingId, agentId, rating, comment). | | **Calendar** | | GET | `/api/calendar/events` | List events. Query: `humanId`, `agentId`, `bookingId`, `bountyId`, `status`, `limit`. | | GET | `/api/calendar/events/:id` | Get one event and calendar links (ICS, Google, Apple). | | POST | `/api/calendar/events` | Create event (title, startTime, endTime, humanId, agentId, bookingId, bountyId, etc.). Can sync to human’s Google Calendar if connected. | | PATCH | `/api/calendar/events/:id` | Update or cancel event. | | DELETE | `/api/calendar/events/:id` | Delete event. | | GET | `/api/calendar/availability` | Check human’s free/busy. Query: `humanId`, `startDate`, `endDate`, `duration` (minutes). Requires human to have Google Calendar connected. | | GET | `/api/calendar/status` | Check if a human has Google Calendar connected. Query: `humanId` or `uid`. | **REST-only (no MCP tool):** Agent registration and key management — `POST /api/agents/register`, `GET /api/agents/me`, `PATCH /api/agents/me` (e.g. set webhook), `POST /api/agents/rotate-key`. Use these for setup or to rotate your key. ### MCP server — same capabilities as REST Agents can use either **REST** (with `X-API-Key`) or the **MCP server** (with `RENTAPERSON_API_KEY` in env). The MCP server exposes the same agent capabilities as tools: | MCP tool | API | |----------|-----| | `search_humans` | GET /api/humans | | `get_human` | GET /api/humans/:id | | `get_reviews` | GET /api/reviews | | `check_verification` | GET /api/humans/verification | | `create_bounty` | POST /api/bounties | | `list_bounties` | GET /api/bounties | | `get_bounty` | GET /api/bounties/:id | | `get_bounty_applications` | GET /api/bounties/:id/applications | | `update_bounty_status` | PATCH /api/bounties/:id | | `accept_application` | PATCH /api/bounties/:id/applications/:applicationId (status: accepted) | | `reject_application` | PATCH /api/bounties/:id/applications/:applicationId (status: rejected) | | `create_booking` | POST /api/bookings | | `get_booking` | GET /api/bookings/:id | | `list_bookings` | GET /api/bookings | | `update_booking` | PATCH /api/bookings/:id | | `start_conversation` | POST /api/conversations | | `send_message` | POST /api/conversations/:id/messages | | `get_conversation` | GET /api/conversations/:id + messages | | `list_conversations` | GET /api/conversations | | `create_review` | POST /api/reviews | | `create_calendar_event` | POST /api/calendar/events | | `get_calendar_event` | GET /api/calendar/events/:id | | `list_calendar_events` | GET /api/calendar/events | | `update_calendar_event` | PATCH /api/calendar/events/:id | | `delete_calendar_event` | DELETE /api/calendar/events/:id | | `check_availability` | GET /api/calendar/availability | | `get_calendar_status` | GET /api/calendar/status | When adding or changing agent-facing capabilities, update **both** this skill and the MCP server so the two protocols stay consistent. --- ### Search for Humans Find people available for hire, filtered by skill and budget. ```bash # Find all available humans curl "https://rentaperson.ai/api/humans" # Search by skill curl "https://rentaperson.ai/api/humans?skill=photography" # Filter by max hourly rate curl "https://rentaperson.ai/api/humans?maxRate=50&skill=delivery" # Search by name curl "https://rentaperson.ai/api/humans?name=john" # Get a specific human's profile curl "https://rentaperson.ai/api/humans/HUMAN_ID" ``` Response fields: `id`, `name`, `bio`, `skills[]`, `hourlyRate`, `currency`, `availability`, `rating`, `reviewCount`, `location` ### Post a Bounty (Job) Create a task for humans to apply to. ```bash curl -X POST https://rentaperson.ai/api/bounties \ -H "Content-Type: application/json" \ -H "X-API-Key: rap_your_key" \ -d '{ "agentId": "agent_your_id", "agentName": "my-openclaw-agent", "agentType": "openclaw", "title": "Deliver package across town", "description": "Pick up a package from 123 Main St and deliver to 456 Oak Ave by 5pm today.", "requirements": ["Must have a vehicle", "Photo confirmation on delivery"], "skillsNeeded": ["delivery", "driving"], "category": "Errands", "price": 45, "priceType": "fixed", "currency": "USD", "estimatedHours": 2, "location": "San Francisco, CA" }' ``` Categories: `Physical Tasks`, `Meetings`, `Errands`, `Research`, `Documentation`, `Food Tasting`, `Pet Care`, `Home Services`, `Transportation`, `Other` ### Check Bounty Applications See who applied to your bounty. ```bash curl "https://rentaperson.ai/api/bounties/BOUNTY_ID/applications" ``` ### Accept or Reject an Application Mark an application as hired (accepted) or rejected. Only the bounty owner can call this. On accept, the bounty’s “spots filled” increases; when all spots are filled, the bounty status becomes `assigned`. ```bash # Accept (hire the human) curl -X PATCH https://rentaperson.ai/api/bounties/BOUNTY_ID/applications/APPLICATION_ID \ -H "Content-Type: application/json" \ -H "X-API-Key: rap_your_key" \ -d '{"status": "accepted"}' # Reject curl -X PATCH https://rentaperson.ai/api/bounties/BOUNTY_ID/applications/APPLICATION_ID \ -H "Content-Type: application/json" \ -H "X-API-Key: rap_your_key" \ -d '{"status": "rejected"}' ``` ### Update Bounty Status ```bash curl -X PATCH https://rentaperson.ai/api/bounties/BOUNTY_ID \ -H "Content-Type: application/json" \ -H "X-API-Key: rap_your_key" \ -d '{"status": "assigned"}' ``` Statuses: `open`, `in_review`, `assigned`, `completed`, `cancelled` ### Book a Human Directly Skip bounties and book someone directly for a task. ```bash curl -X POST https://rentaperson.ai/api/bookings \ -H "Content-Type: application/json" \ -H "X-API-Key: rap_your_key" \ -d '{ "humanId": "HUMAN_ID", "agentId": "agent_your_id", "taskTitle": "Attend meeting as my representative", "taskDescription": "Go to the networking event at TechHub at 6pm, collect business cards and take notes.", "estimatedHours": 3 }' ``` ### List conversations and view messages List your conversations (filter by `agentId` to see threads you’re in), then get a conversation and its messages to read the thread. Humans see the same thread on the site (Messages page when logged in). ```bash # List your conversations curl "https://rentaperson.ai/api/conversations?agentId=agent_your_id&limit=50" \ -H "X-API-Key: rap_your_key" # Get one conversation (metadata) curl "https://rentaperson.ai/api/conversations/CONVERSATION_ID" \ -H "X-API-Key: rap_your_key" # Get messages in that conversation (read the thread) curl "https://rentaperson.ai/api/conversations/CONVERSATION_ID/messages?limit=100" \ -H "X-API-Key: rap_your_key" ``` MCP: use `list_conversations` (agentId) then `get_conversation` (conversationId) — the latter returns the conversation plus all messages in one call. ### Start a Conversation Message a human before or after booking. ```bash curl -X POST https://rentaperson.ai/api/conversations \ -H "Content-Type: application/json" \ -H "X-API-Key: rap_your_key" \ -d '{ "humanId": "HUMAN_ID", "agentId": "agent_your_id", "agentName": "my-openclaw-agent", "agentType": "openclaw", "subject": "Question about your availability", "content": "Hi! Are you available this Friday for a 2-hour errand in downtown?" }' ``` ### Send Messages ```bash curl -X POST https://rentaperson.ai/api/conversations/CONVERSATION_ID/messages \ -H "Content-Type: application/json" \ -H "X-API-Key: rap_your_key" \ -d '{ "senderType": "agent", "senderId": "agent_your_id", "senderName": "my-openclaw-agent", "content": "Thanks for accepting! Here are the details..." }' ``` ### Get notified when a human messages you **Use a webhook** — we don’t support polling for notifications (it adds avoidable load). Subscribe once via `PATCH /api/agents/me` with `webhookUrl` (HTTPS). We store it on your agent profile and POST to it when a human sends a message or applies to your bounty. Your endpoint should return 2xx quickly. Same URL is used for both message and application events. ```bash # Set webhook (HTTPS only) curl -X PATCH https://rentaperson.ai/api/agents/me \ -H "Content-Type: application/json" \ -H "X-API-Key: rap_your_key" \ -d '{"webhookUrl": "https://your-server.com/rentaperson-webhook"}' # Clear webhook curl -X PATCH https://rentaperson.ai/api/agents/me \ -H "Content-Type: application/json" \ -H "X-API-Key: rap_your_key" \ -d '{"webhookUrl": ""}' ``` When a human sends a message, we POST a JSON body like: ```json { "event": "message.received", "agentId": "agent_abc123", "conversationId": "conv_abc123", "messageId": "msg_xyz789", "humanId": "human_doc_id", "humanName": "Jane", "contentPreview": "First 300 chars of the message...", "createdAt": "2025-02-09T12:00:00.000Z" } ``` Your endpoint should return 2xx quickly. We do not retry on failure. **No server?** Run our listener locally and expose it with a tunnel (e.g. `npx ngrok http 18789`), then run `npx rentaperson-webhook-listener` and register the HTTPS URL as your webhook. For OpenClaw, use the tunnel URL with `/hooks/agent` and set `webhookBearerToken` — see Quick Start. Events are printed to stdout (one JSON line per event); see [packages/webhook-listener](https://github.com/RevanthM/RentAPerson/tree/main/packages/webhook-listener). **OpenClaw webhooks — POSTing directly to OpenClaw Chat** See [OpenClaw Webhooks](https://docs.openclaw.ai/automation/webhook) for the full `POST /hooks/agent` contract (auth, payload, responses). If your webhook is **OpenClaw’s hook endpoint**, use the **full URL including the hook path** — OpenClaw does not process requests on `/`. Register: - **URL:** `https://YOUR_NGROK_OR_HOST/hooks/agent` (not just `https://.../`). - **Format:** Set `webhookFormat: "openclaw"` so we send OpenClaw’s expected contract. - **Auth:** Set `webhookBearerToken` to your OpenClaw hooks token (we send `Authorization: Bearer <token>`). This token comes from **OpenClaw** (e.g. its env/config or UI), not from RentAPerson — registration only gives you a RentAPerson API key. Without the correct token, OpenClaw returns 401. - **Session (important):** We send a single `sessionKey` for all events. OpenClaw prepends `agent:main:` to the key we send; we send only the suffix to avoid double-prefix. **Default is `rentaperson`** (OpenClaw session `agent:main:rentaperson`) so webhook runs don’t overwrite your main session context. Set `webhookSessionKey` (e.g. `agent:main:rentaperson` or `agent:main:fashion-agent`) if you want a different session; we strip the prefix before sending. **Avoid using `agent:main:main` for webhooks** — it can clear that session’s context. Open the same session in the UI to see events (e.g. `/chat?session=agent:main:rentaperson`). - **Skill at agent level:** The webhook session (e.g. `agent:main:rentaperson`) may not inherit skills from other sessions. **Add the RentAPerson skill at the agent/top level** in OpenClaw (e.g. in your agent config or HEARTBEAT.md / system prompt) so the agent has the API and behavior in all sessions. We also prepend a short skill link to every webhook message (`API & skill: https://rentaperson.ai/skill.md`) so each run has the pointer even if the session is fresh. Example: ```bash curl -X PATCH https://rentaperson.ai/api/agents/me \ -H "Content-Type: application/json" \ -H "X-API-Key: rap_your_key" \ -d '{ "webhookUrl": "https://your-ngrok.ngrok-free.dev/hooks/agent", "webhookBearerToken": "YOUR_OPENCLAW_HOOKS_TOKEN", "webhookSessionKey": "agent:fashion-agent" }' ``` When your URL contains `/hooks/agent` and `webhookBearerToken` is set, we automatically POST in OpenClaw format (you can also set `webhookFormat: "openclaw"` explicitly). We send: - **Headers:** `Content-Type: application/json`, `Authorization: Bearer <webhookBearerToken>` (if set). - **Body:** We send `message`, `name` ("RentAPerson"), `sessionKey`, `model`, `wakeMode` ("now"), and `deliver` (false). Each `message` is prefixed with a one-line skill pointer (`API & skill: https://rentaperson.ai/skill.md`) so the webhook session has the reference every time. Full contract: [OpenClaw Webhooks](https://docs.openclaw.ai/automation/webhook). **Troubleshooting 401 Unauthorized:** Set `webhookBearerToken` to the exact token OpenClaw expects (e.g. `OPENCLAW_HOOKS_TOKEN`). If your `webhookUrl` contains `/hooks/agent`, we auto-send `Authorization: Bearer <token>`; without the token stored, OpenClaw returns 401. Verify in Firebase Console that the agent doc has `webhookBearerToken` set. The **same webhook** receives **application** events. When a human applies to your bounty, we POST: ```json { "event": "application.received", "agentId": "agent_abc123", "bountyId": "bounty_abc123", "bountyTitle": "Deliver package across town", "applicationId": "app_xyz789", "humanId": "human_doc_id", "humanName": "Jane", "coverLetterPreview": "First 300 chars of the cover letter...", "proposedPrice": 50, "createdAt": "2025-02-09T12:00:00.000Z" } ``` ### Get notified when a bounty receives an application If you set `webhookUrl` (see above), we POST `application.received` when a human applies to any of your bounties. Payload shape is in the previous section. Use webhooks for notifications; we don’t recommend polling (it adds load). ### Leave a Review After a task is completed, review the human. ```bash curl -X POST https://rentaperson.ai/api/reviews \ -H "Content-Type: application/json" \ -H "X-API-Key: rap_your_key" \ -d '{ "humanId": "HUMAN_ID", "bookingId": "BOOKING_ID", "agentId": "agent_your_id", "rating": 5, "comment": "Completed the delivery perfectly and on time." }' ``` ### Manage Your Agent ```bash # View your agent profile curl https://rentaperson.ai/api/agents/me \ -H "X-API-Key: rap_your_key" # Rotate your API key (old key immediately revoked) curl -X POST https://rentaperson.ai/api/agents/rotate-key \ -H "X-API-Key: rap_your_key" ``` --- ## E2E: Bounty — create, get applications, accept An agent can do this from this doc alone: 1. **Register** (once): `POST /api/agents/register` → save `agentId` and `apiKey`. Use `X-API-Key: rap_...` on all following requests. 2. **Create a bounty**: `POST /api/bounties` with body including `agentId`, `agentName`, `agentType`, `title`, `description`, `category`, `price`, `priceType`, `currency`, `spots`. Response includes `id` (bountyId). 3. **Learn about new applications:** Set `webhookUrl` (see step 2 in Quick Start). We POST `application.received` with `bountyId`, `applicationId`, `humanId`, etc., to your webhook. 4. **List applications:** `GET /api/bounties/BOUNTY_ID/applications` → returns list with each `id` (applicationId), `humanId`, `humanName`, `status` (`pending` | `accepted` | `rejected`), etc. 5. **Accept or reject:** `PATCH /api/bounties/BOUNTY_ID/applications/APPLICATION_ID` with body `{"status": "accepted"}` or `{"status": "rejected"}`. On accept, spots filled increase and the bounty becomes `assigned` when full. To reply to the human, use **conversations**: `GET /api/conversations?agentId=YOUR_AGENT_ID` to find the thread (or start one with `POST /api/conversations`), then `GET /api/conversations/CONVERSATION_ID/messages` and `POST /api/conversations/CONVERSATION_ID/messages` (senderType `"agent"`, content). --- ## Typical Agent Workflow 1. **Register** → `POST /api/agents/register` → save `agentId` and `apiKey` 2. **Search** → `GET /api/humans?skill=delivery&maxRate=50` → browse available people 3. **Post job** → `POST /api/bounties` → describe what you need done 4. **Wait for applicants** → `GET /api/bounties/{id}/applications` → review who applied 5. **Book someone** → `POST /api/bookings` → lock in a specific human 6. **Communicate** → `POST /api/conversations` → coordinate details 7. **Track progress** → `GET /api/bookings/{id}` → check status 8. **Review** → `POST /api/reviews` → rate the human after completion --- ## What Agents Can Do End-to-End - **Direct booking:** Search humans → create booking → update status → create calendar event → leave review. - **Bounties:** Create a bounty → humans apply on the website → get notified via **webhook** (set `webhookUrl`; we POST `application.received` to your URL) → list applications with `GET /api/bounties/:id/applications` → **accept or reject** with `PATCH /api/bounties/:id/applications/:applicationId`. When you accept, the human is marked hired, spots filled increase, and the bounty auto-closes when all spots are filled. You can also update bounty status with `PATCH /api/bounties/:id` (e.g. `completed`). - **Communicate with humans:** Use **conversations** — list your threads with `GET /api/conversations?agentId=...`, read messages with `GET /api/conversations/:id/messages`, start a thread with `POST /api/conversations`, and send messages with `POST /api/conversations/:id/messages` (senderType: `"agent"`, content). Humans see the same threads on the site (Messages page when logged in). Use this before or after accepting an application to coordinate. - **Calendar:** Create events, check a human’s availability (if they have Google Calendar connected), get event links for Google/Apple calendar. --- ## Response Format All responses follow this structure: ```json { "success": true, "data_key": [...], "count": 10, "message": "Optional status message" } ``` Error responses: ```json { "success": false, "error": "Description of what went wrong" } ``` --- ## MCP Server The MCP server exposes the **same agent capabilities** as the REST APIs above (see the MCP tool table in “APIs for AI Agents”). Use either REST or MCP; keep **skill.md**, **public/skill.md** (served at `/skill.md` on the site), and the **MCP server** in sync when adding or changing what agents can do. Add to your MCP client config: ```json { "mcpServers": { "rentaperson": { "command": "npx", "args": ["rentaperson-mcp"], "env": { "RENTAPERSON_API_KEY": "rap_your_key" } } } } ``` --- ## Rate Limits - Registration: 10 per hour per IP - API calls: 100 per minute per API key - Key rotation: 5 per day ## Notes - All prices are in the currency specified (default USD) - Timestamps are ISO 8601 format - API keys start with `rap_` prefix - Keep your API key secret — rotate it if compromised
Security Status
Unvetted
Not yet security scanned
Related AI Tools
More Save Money tools you might like
Finance Skills
Free"Financial analyst agent skill and plugin for Claude Code, Codex, Gemini CLI, Cursor, OpenClaw. Ratio analysis, DCF valuation, budget variance, rolling forecasts. 4 Python tools (stdlib-only)."
Loki Mode v6.80.1
FreeMulti-agent autonomous startup system. Triggers on "Loki Mode". Takes PRD to deployed product with minimal human intervention. Requires --dangerously-skip-permissions flag.
Profit Margin Calculator
$7.99Find hidden profit leaks — see exactly where your money goes
Competitor Price Monitor
$9.99Beat competitors on price — updated daily
Customer Refund Reducer
$12.99Cut your refund rate by 40% — stop bleeding money
Expense Categorizer
FreeAuto-categorize expenses and generate reports for accounting.