1. Read a published portfolio (no auth)
The headless read API returns the same data we render at /<username>/ as JSON. CORS is open, so you can call it directly from any browser.
curl
curl https://quilix.io/api/v1/public/yahya/portfolio/
JavaScript (fetch)
const res = await fetch(
"https://quilix.io/api/v1/public/yahya/portfolio/",
);
const portfolio = await res.json();
console.log(portfolio.owner.full_name);
console.log(portfolio.projects.length);
Python (requests)
import requests
r = requests.get("https://quilix.io/api/v1/public/yahya/portfolio/")
r.raise_for_status()
data = r.json()
print(data["owner"]["full_name"])
Response shape
{
"owner": { "username": "yahya", "full_name": "...", "avatar": null },
"portfolio": {
"headline": "Senior Backend Engineer",
"tagline": "Django + DRF",
"bio": "...",
"is_published": true,
"view_count": 0
},
"social_links": [...],
"education": [...],
"experience": [...],
"projects": [...],
"skills": [...],
"certifications": [...]
}
Returns 404 if the user doesn't exist or the portfolio is unpublished. See Errors.
2. Write data via the dashboard API (JWT)
Login (or sign up) gets you a short-lived access token + a longer-lived refresh token. Send the access token as a Bearer header on protected endpoints.
Sign up
curl -X POST https://quilix.io/api/v1/auth/register/email/ \
-H "Content-Type: application/json" \
-d '{
"email": "you@example.com",
"password": "very-long-password",
"full_name": "Your Name",
"username": "yourname"
}'
Log in
curl -X POST https://quilix.io/api/v1/auth/login/email/ \
-H "Content-Type: application/json" \
-d '{"email":"you@example.com","password":"very-long-password"}'
# → { "tokens": { "access": "...", "refresh": "..." }, "user": { ... } }
Update your portfolio
curl -X PATCH https://quilix.io/api/v1/portfolio/ \
-H "Authorization: Bearer <access-token>" \
-H "Content-Type: application/json" \
-d '{"headline":"Senior Backend Engineer","tagline":"Django + DRF"}'
Refresh an expired access token
curl -X POST https://quilix.io/api/v1/auth/refresh/ \
-H "Content-Type: application/json" \
-d '{"refresh":"<refresh-token>"}'
Access tokens last 15 minutes; refresh tokens last 14 days and rotate on use. See Authentication for the full picture.
3. Programmatic writes with a Personal Access Token
For CI, syncing from a CMS, or scripts you don't want to re-authenticate every 14 days, generate a long-lived Personal Access Token in the dashboard at /app/account/access-tokens. It looks like phub_pat_… and authenticates the same way as a JWT access token.
# Issue
curl -X POST https://quilix.io/api/v1/me/access-tokens/ \
-H "Authorization: Bearer <jwt-access>" \
-H "Content-Type: application/json" \
-d '{"name":"ci-content-sync"}'
# → { "token": "phub_pat_xxxx...", "name": "ci-content-sync", "prefix": "phub_pat_xxxx", "id": "..." }
# IMPORTANT: the plaintext token is shown ONCE. Store it in a secret manager.
# Use
curl -X POST https://quilix.io/api/v1/portfolio/projects/ \
-H "Authorization: Bearer phub_pat_xxxx..." \
-H "Content-Type: application/json" \
-d '{"title":"My CI-built project","summary":"Synced from a CMS"}'
PATs never expire by default; revoke them from the dashboard the moment one leaks. Server logs include only the prefix, never the plaintext.
Next steps
- Browse every endpoint in the interactive reference.
- Read Authentication in depth.
- Understand the unified error envelope — frontends should branch on
error.code. - Plan around rate limits — different scopes for anon, user, auth, OTP, public read.