Public API · v1

Bring your project data into your own systems

A read-only REST API to read a project's issues, RFIs, change orders, non-conformances, meetings and forms from your BI, ERP or scripts.

Overview

Each credential belongs to one project and is created by an owner or admin in Project settings › API. It has a public client ID, a client secret shown only once, and a list of permissions chosen per module.

Your server exchanges the credential for a short-lived access token and sends it on every request. The API is read-only: no endpoint creates, changes or deletes data.

Authentication

OAuth 2.0 client credentials

Format

JSON, UTF-8

Token lifetime

1 hour, no refresh token

Access

Read-only, one project per credential

Base URL: https://api.wayki.app

Authentication

We use the OAuth 2.0 client-credentials flow (RFC 6749 §4.4). The secret only travels to the token endpoint; every data request carries a token that expires on its own.

  1. 1. Create a credential

    In Wayki, open the project › Settings › API › New credential. Pick only the permissions you need and, when you can, restrict by IP and set an expiry date. Copy the client secret: it will not be shown again. If you don't see the API tab, ask Wayki to enable the API module for your project.

  2. 2. Request a token

    Send client_id and client_secret with HTTP Basic (recommended) or in the body, with grant_type=client_credentials. Optionally ask for fewer permissions with scope.

  3. 3. Call the API

    Send the token in the Authorization: Bearer header. Reuse it until shortly before expires_in, then request another.

Get a token
Shell
curl -X POST https://api.wayki.app/v1/oauth/token -u 'CLIENT_ID:CLIENT_SECRET' -d grant_type=client_credentials
Response
JSON
{
  "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "scope": "issues:read rfis:read"
}
Call an endpoint
Shell
curl 'https://api.wayki.app/v1/projects/64b7f0c2e4b0a1b2c3d4e5f6/issues?page=1&limit=50' -H 'Authorization: Bearer ACCESS_TOKEN'
200 OK
JSON
{
  "data": [
    { "_id": "66f1…", "issueNumber": 128, "title": "Fisura en losa N3", "statusKey": "open", … }
  ],
  "meta": { "page": 1, "limit": 50, "total": 312, "totalPages": 7 }
}
Node.js (with token caching)
JavaScript
const BASE = "https://api.wayki.app";
const PROJECT = process.env.WAYKI_PROJECT_ID;
let cached = { token: null, expiresAt: 0 };

async function getToken() {
  // Reuse the token until one minute before it expires.
  if (cached.token && Date.now() < cached.expiresAt - 60_000) return cached.token;
  const basic = Buffer.from(
    `${process.env.WAYKI_CLIENT_ID}:${process.env.WAYKI_CLIENT_SECRET}`,
  ).toString("base64");
  const res = await fetch(`${BASE}/v1/oauth/token`, {
    method: "POST",
    headers: {
      Authorization: `Basic ${basic}`,
      "Content-Type": "application/x-www-form-urlencoded",
    },
    body: "grant_type=client_credentials",
  });
  if (!res.ok) throw new Error(`Token request failed: ${res.status}`);
  const body = await res.json();
  cached = { token: body.access_token, expiresAt: Date.now() + body.expires_in * 1000 };
  return cached.token;
}

async function listAllRfis() {
  const all = [];
  for (let page = 1; ; page++) {
    const res = await fetch(
      `${BASE}/v1/projects/${PROJECT}/rfis?page=${page}&limit=100`,
      { headers: { Authorization: `Bearer ${await getToken()}` } },
    );
    if (!res.ok) throw new Error(`RFIs request failed: ${res.status}`);
    const { data, meta } = await res.json();
    all.push(...data);
    if (page >= meta.totalPages) return all;
  }
}
Python
Python
import os, requests

BASE = "https://api.wayki.app"
token = requests.post(
    f"{BASE}/v1/oauth/token",
    auth=(os.environ["WAYKI_CLIENT_ID"], os.environ["WAYKI_CLIENT_SECRET"]),
    data={"grant_type": "client_credentials", "scope": "meetings:read"},
    timeout=10,
).json()["access_token"]

meetings = requests.get(
    f"{BASE}/v1/projects/{os.environ['WAYKI_PROJECT_ID']}/meetings",
    headers={"Authorization": f"Bearer {token}"},
    params={"from": "2026-09-01", "to": "2026-09-30"},
    timeout=10,
).json()["data"]
  • Replace CLIENT_ID, CLIENT_SECRET and ACCESS_TOKEN with your values. Commands are one line and work the same in bash and PowerShell; in Windows PowerShell 5.1 type curl.exe instead of curl.
  • There is no refresh token: when the token expires, request a new one with the credential.
  • The token only works for the credential's project. Another projectId answers 403.
  • Revoking the credential, rotating its secret, removing a permission or archiving the project cuts live tokens off immediately.
  • The token endpoint allows 10 requests per minute per IP: keep the token in memory instead of requesting one per call.

Try it in Postman

The collection has every endpoint grouped by module and fetches the token for you: just fill in your credentials.

  1. 1. Import the collection

    Download it and in Postman use Import › File. It shows up as “Wayki Project API (v1)”.

  2. 2. Fill in the variables

    Open the collection › Variables and set your clientId, clientSecret and projectId as Current value. Change baseUrl if you use another server.

  3. 3. Send any request

    A collection script gets the token, stores it and renews it before it expires. List filters come disabled so you can switch them on.

Keep the client secret only as Current value: Postman neither syncs it nor includes it on export. Don't share the collection with real values.

Download collection

Permissions (scopes)

Every endpoint requires exactly one permission. A credential only sees the modules it was granted and, inside them, everything a project owner would see.

ScopeWhat it can read
project:readProject — Project name, code, status, location and company.
members:readMembers — Members and roles, with email. Also reveals people's emails in every other resource.
issues:readIssues — Issues, settings and replies.
rfis:readRFIs — RFIs, settings and replies.
change-orders:readChange orders — Change orders, settings and replies.
non-conformances:readNon-conformances — Non-conformances, settings and replies.
meetings:readMeetings — Meetings, agreements and frozen minutes.
forms:readForms — Filled-in forms and their settings.

Pagination and errors

Response format

Lists return { data, meta } and single resources { data }. Ids are 24-character hex ObjectIds and dates are ISO 8601 in UTC. Without members:read, people appear without email; phone numbers are never exposed.

List parameters

page
Page, from 1. Defaults to 1.
limit
Items per page, 1 to 100. Defaults to 20.
sort
Sort field. Each resource has its own allowed list; anything else answers 400.
order
asc or desc. Defaults to desc.
q
Free-text search.
from / to
Inclusive ISO 8601 date range.

Data API errors

Errors return { statusCode, message, error }. An unknown query parameter is a 400, not ignored.

  • 400Invalid or unknown parameter.
  • 401Missing or expired token, or revoked credential. Request a new token.
  • 403Missing permission, or the token belongs to another project.
  • 404The resource does not exist in this project.
  • 429Rate limited. Retry after Retry-After.

Token endpoint errors (RFC 6749)

  • invalid_client (401)

    Wrong client ID or secret, or revoked or expired credential.

  • invalid_scope (400)

    You asked for a scope the credential does not hold.

  • unauthorized_client (400)

    Archived project or IP outside the allow-list.

  • invalid_request (400)

    Missing parameters, or the credential was sent twice.

  • unsupported_grant_type (400)

    Only client_credentials is supported.

Reference

Every route starts with /v1/projects/{projectId}. The downloadable OpenAPI describes the same endpoints.

Project and members

Permission: project:read · members:read

General project data and the member list with roles.

  • GET/v1/projects/{projectId}The project (project:read).
  • GET/v1/projects/{projectId}/membersMembers and roles, not paged (members:read).
Example
Shell
curl 'https://api.wayki.app/v1/projects/64b7f0c2e4b0a1b2c3d4e5f6' -H 'Authorization: Bearer ACCESS_TOKEN'

Issues

Permission: issues:read

Paged list, detail, replies and settings (statuses, priorities, types) of issues.

  • GET/v1/projects/{projectId}/issuesPaged list.
  • GET/v1/projects/{projectId}/issues/{issueId}Detail.
  • GET/v1/projects/{projectId}/issues/{issueId}/repliesReplies, oldest first (order=desc reverses).
  • GET/v1/projects/{projectId}/issues/settingsModule settings: catalogs to interpret the fields.

Allowed sort: createdAt, updatedAt, number, title, dueDate, priority. from / to filter on creation date.

Example
Shell
curl 'https://api.wayki.app/v1/projects/64b7f0c2e4b0a1b2c3d4e5f6/issues?limit=50&sort=updatedAt' -H 'Authorization: Bearer ACCESS_TOKEN'

RFIs

Permission: rfis:read

Paged list, detail, replies and settings (statuses, priorities, types) of RFIs.

  • GET/v1/projects/{projectId}/rfisPaged list.
  • GET/v1/projects/{projectId}/rfis/{rfiId}Detail.
  • GET/v1/projects/{projectId}/rfis/{rfiId}/repliesReplies, oldest first (order=desc reverses).
  • GET/v1/projects/{projectId}/rfis/settingsModule settings: catalogs to interpret the fields.

Allowed sort: createdAt, updatedAt, number, title, dueDate, status, priority. from / to filter on creation date.

Example
Shell
curl 'https://api.wayki.app/v1/projects/64b7f0c2e4b0a1b2c3d4e5f6/rfis?limit=50&sort=updatedAt' -H 'Authorization: Bearer ACCESS_TOKEN'

Change orders

Permission: change-orders:read

Paged list, detail, replies and settings (statuses, priorities, types) of change orders.

  • GET/v1/projects/{projectId}/change-ordersPaged list.
  • GET/v1/projects/{projectId}/change-orders/{changeOrderId}Detail.
  • GET/v1/projects/{projectId}/change-orders/{changeOrderId}/repliesReplies, oldest first (order=desc reverses).
  • GET/v1/projects/{projectId}/change-orders/settingsModule settings: catalogs to interpret the fields.

Allowed sort: createdAt, updatedAt, number, title, dueDate, status, priority. from / to filter on creation date.

Example
Shell
curl 'https://api.wayki.app/v1/projects/64b7f0c2e4b0a1b2c3d4e5f6/change-orders?limit=50&sort=updatedAt' -H 'Authorization: Bearer ACCESS_TOKEN'

Non-conformances

Permission: non-conformances:read

Paged list, detail, replies and settings (statuses, priorities, types) of non-conformances.

  • GET/v1/projects/{projectId}/non-conformancesPaged list.
  • GET/v1/projects/{projectId}/non-conformances/{nonConformanceId}Detail.
  • GET/v1/projects/{projectId}/non-conformances/{nonConformanceId}/repliesReplies, oldest first (order=desc reverses).
  • GET/v1/projects/{projectId}/non-conformances/settingsModule settings: catalogs to interpret the fields.

Allowed sort: createdAt, updatedAt, number, title, dueDate, status, priority. from / to filter on creation date.

Example
Shell
curl 'https://api.wayki.app/v1/projects/64b7f0c2e4b0a1b2c3d4e5f6/non-conformances?limit=50&sort=updatedAt' -H 'Authorization: Bearer ACCESS_TOKEN'

Meetings

Permission: meetings:read

Meetings with agenda, participants and agreements, plus the frozen minutes of each closed session.

  • GET/v1/projects/{projectId}/meetingsPaged list.
  • GET/v1/projects/{projectId}/meetings/{meetingId}Detail with agreements and participants.
  • GET/v1/projects/{projectId}/meetings/{meetingId}/minutesFrozen minutes (one per closed session).
  • GET/v1/projects/{projectId}/meetings/{meetingId}/minutes/{versionId}One set of minutes.
  • GET/v1/projects/{projectId}/meetings/settingsAgreement statuses and priorities.

Allowed sort: startPlannedDate, createdAt, title, number. from / to filter on the planned start date.

Example
Shell
curl 'https://api.wayki.app/v1/projects/64b7f0c2e4b0a1b2c3d4e5f6/meetings?from=2026-09-01&to=2026-09-30' -H 'Authorization: Bearer ACCESS_TOKEN'

Forms

Permission: forms:read

Filled-in forms (inspections, checklists, permits) and their settings.

  • GET/v1/projects/{projectId}/formsPaged list.
  • GET/v1/projects/{projectId}/forms/{formId}Detail.
  • GET/v1/projects/{projectId}/forms/settingsStatuses, priorities and types.

Allowed sort: createdAt, updatedAt, number, name. from / to filter on creation date.

Example
Shell
curl 'https://api.wayki.app/v1/projects/64b7f0c2e4b0a1b2c3d4e5f6/forms?q=concreto' -H 'Authorization: Bearer ACCESS_TOKEN'

Limits

Data requests
100 per minute per credential and endpoint
Token endpoint
10 per minute per IP
Page size
100 items at most
Active credentials
10 per project
Token lifetime
3600 seconds

Security

A credential gives access to real project data. These rules prevent most incidents.

Do

  • Keep the client secret in a secrets manager or in server environment variables.
  • Create one credential per integration, with the fewest permissions.
  • Restrict by IP when your integration calls from fixed addresses.
  • Set an expiry date on third-party or temporary credentials.
  • Rotate the secret when someone who knew it leaves the team.

Don't

  • Don't call the API from a browser or a mobile app: the secret would be exposed. The API does not allow CORS.
  • Don't put credentials in URLs or code repositories.
  • Don't share one credential across integrations: you won't know which to revoke.
  • Don't log the secret or the Authorization header.

How we protect the data

  • The secret is stored as a SHA-256 hash: not even Wayki can show it again.
  • Tokens are signed with a key exclusive to this API and bound to one project.
  • Every request re-checks the credential, the project and the IP: revoking cuts access at once.
  • Credentials can only reach the read-only /v1 routes; the rest of the API rejects them.
  • Every creation, change, rotation, revocation and deletion is recorded with its author.

Changelog

  • v1 · 2026-09

    First release: project, members, issues, RFIs, change orders, non-conformances, meetings and forms, read-only.