REST API v1
Everything the app does, you can do over HTTP. Scoped keys, predictable JSON, cursor pagination and signed webhooks — on every plan, including Free.
Base URL
https://manageyourproject.net/api/v1
Authentication
Create a key in Settings → API. Keys are shown once; we store only a hash. Choose the narrowest scope that
works: read, write or admin.
curl https://manageyourproject.net/api/v1/tasks \
-H "X-Api-Key: myp_live_2f8c...";
A bearer token is also accepted, which is handy for OAuth-style integrations:
curl https://manageyourproject.net/api/v1/tasks -H "Authorization: Bearer myp_live_2f8c..."
Conventions
- JSON in, JSON out. Send
Content-Type: application/jsonon writes. PATCHis a partial update; omitted fields are untouched.- Timestamps are UTC ISO-8601. Dates are
YYYY-MM-DD. - Every response carries
X-Request-Id. Quote it in support requests. - IDs are integers, scoped to your organisation. There is no way to read another tenant's row.
Pagination
Cursor-based, because offsets drift while people are working.
GET https://manageyourproject.net/api/v1/tasks?limit=100&cursor=eyJpZCI6MTgzMn0
{
"data": [ { "id": 1833, "title": "..." } ],
"meta": { "count": 100, "has_more": true, "next_cursor": "eyJpZCI6MTkzM30" }
}
Pass updated_since to build an incremental sync. Combined with webhooks that gives you a reliable
mirror without polling everything.
Filtering
GET https://manageyourproject.net/api/v1/tasks
?project_id=12
&status_category=in_progress
&assignee_id=8
&due_before=2026-08-26
&tag=billable
&sort=-priority,due_date
Resources
| Resource | Endpoints | Notes |
|---|---|---|
/projects | GET, POST, PATCH, DELETE | Include ?with=stats for task and time rollups |
/tasks | GET, POST, PATCH, DELETE | Accepts assignees[], tags[], custom_fields{} |
/tasks/{id}/comments | GET, POST | Mentions resolve from @email |
/time-entries | GET, POST, PATCH, DELETE | duration_min or started_at/ended_at |
/documents | GET, POST, PATCH | HTML body is sanitised server-side |
/invoices | GET, POST, PATCH | POST /invoices/from-time bills unbilled hours |
/clients | GET, POST, PATCH | Returns the portal URL when one exists |
/goals | GET, POST, PATCH | Progress is derived from linked work |
/users | GET | Members of your organisation only |
/webhooks | GET, POST, DELETE | Returns the signing secret once, on create |
Create a task
curl -X POST https://manageyourproject.net/api/v1/tasks \
-H "X-Api-Key: $KEY" -H "Content-Type: application/json" \
-d '{
"project_id": 12,
"title": "Instrument the checkout funnel",
"description": "<p>Events for view, add, purchase.</p>",
"priority": 3,
"estimated_hours": 8,
"due_date": "2026-08-22",
"assignees": [8, 14],
"tags": ["analytics", "billable"],
"custom_fields": { "client_ref": "ACME-2211" }
}'
Errors
{
"error": {
"code": "validation_failed",
"message": "due_date must be on or after start_date",
"field": "due_date"
}
}
| Status | Code | Meaning |
|---|---|---|
| 400 | bad_request | Malformed JSON or unknown parameter |
| 401 | unauthenticated | Missing, revoked or malformed key |
| 403 | forbidden | Key scope or workspace role is insufficient |
| 403 | plan_limit | Plan entitlement exceeded; body names the limit |
| 404 | not_found | No such record in your organisation |
| 422 | validation_failed | Field-level validation error |
| 429 | rate_limited | Retry after the seconds in Retry-After |
Rate limits
Per key, per minute: 60 on Free, 300 on Pro, 1,000 on Business, 5,000 on Enterprise. Every response includes
X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset. Back off on
429 rather than retrying immediately — repeated hammering extends the window.
Webhooks
Register an endpoint, pick events, verify the signature. Payloads are delivered within seconds and retried with exponential backoff for 24 hours; an endpoint failing continuously is disabled and you are emailed.
POST /webhooks
{
"url": "https://example.com/hooks/myp",
"events": ["task.created", "task.status_changed", "time.logged", "invoice.paid"]
}
Verify in PHP:
$raw = file_get_contents('php://input');
$sig = $_SERVER['HTTP_X_MYP_SIGNATURE'] ?? '';
$ts = $_SERVER['HTTP_X_MYP_TIMESTAMP'] ?? '';
if (abs(time() - (int) $ts) > 300) {
http_response_code(400);
exit; // replay window exceeded
}
$expected = hash_hmac('sha256', $ts . '.' . $raw, $secret);
if (!hash_equals($expected, $sig)) {
http_response_code(401);
exit;
}
http_response_code(200); // ack fast, then process in a queue
Idempotency
Send Idempotency-Key on POST and we return the original response for 24 hours instead of creating a
duplicate. Use it whenever a retry is possible, which is always.
Client libraries
Generate one from the OpenAPI 3 spec:
npx @openapitools/openapi-generator-cli generate \
-i https://manageyourproject.net/openapi.json \
-g typescript-fetch -o ./src/myp