← Back to Home

Workways Public API

REST API Documentation for Public Sites and Rooms

Overview

The Workways Public API provides access to information about public coworking sites and meeting rooms. This API is designed for developers who want to integrate Workways public spaces data into their applications.

Base URL: https://api.workways.com

Authentication

Authentication Policy:
  • GET endpoints (reading data): Public, no authentication required
  • POST endpoints (creating/modifying data): Require API token authentication

Obtaining API Tokens

To access POST endpoints, you need an API token. Here's how to get one:

  1. Contact Workways: Request API access by emailing appsupport@workways.com
  2. Account Setup: Your account will be configured with the api-developer or app-admin role
  3. Generate Token: Log in to your Workways account and navigate to Settings → API Developer Settings
  4. Create Token: Click "Create Token", give it a descriptive name, and copy the generated token
  5. Store Securely: Save the token in a secure location (e.g., environment variables). The token is only shown once and cannot be retrieved later.
API Token Features:
  • Format: ww_dev_<64-character-hex-string>
  • Security: Tokens are stored as SHA-256 hashes and never exposed in plain text after creation
  • Management: Create multiple tokens, track last usage, revoke tokens anytime
  • Optional Quotas: Daily and monthly API usage limits can be configured per user

Token Management

Token Regeneration

If your token is compromised or lost, you can regenerate it while keeping the same token ID and tracking history:

  1. Log in to your Workways account
  2. Navigate to Settings → API Developer Settings
  3. Click the refresh icon next to the token you want to regenerate
  4. Confirm the regeneration (the old token will become immediately invalid)
  5. Copy and securely store the new token (shown only once)
Token Regeneration Behavior:
  • Keeps the same token ID for tracking purposes
  • Preserves the original creation date
  • Generates a new token hash and prefix
  • Old token becomes invalid immediately
  • New token is shown once and cannot be retrieved later
Token Deletion and Invalidation

Tokens can be removed in two ways depending on who performs the action:

Deletion Behavior:
  • User deletes their own token: The token is permanently deleted from the database
  • Admin deletes another user's token: The token is invalidated (isActive=false) but remains in the database for tracking purposes

Using API Tokens

Include your API token in the Authorization header of your requests using the Bearer authentication scheme:

curl -X POST "https://api.workways.com/api/register" \
  -H "Authorization: Bearer ww_dev_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{...}'

Endpoints Requiring Authentication

Endpoint Method Authentication Required
/api/sites GET ⚡ Optional
/api/rooms GET ⚡ Optional
/api/countries GET ❌ No
/api/icsfeed GET ❌ No
/api/register POST ✅ Yes
/api/meeting-request POST ✅ Yes
/api/check-email POST ✅ Yes
/api/room-price GET ✅ Yes
/api/contract-options GET ✅ Yes
/api/user-info GET ✅ Yes
/api/set-optin POST ✅ Yes
/api/organization-info GET ✅ Yes
/api/merge-contract POST ✅ Yes

Authentication Error Responses

Missing Authorization Header
{
  "retcode": -1,
  "message": "Unauthorized: Authorization header is required. Use: Authorization: Bearer <your_token>"
}
Invalid Authorization Format
{
  "retcode": -1,
  "message": "Unauthorized: Invalid authorization format. Use: Authorization: Bearer <your_token>"
}
Invalid or Revoked Token
{
  "retcode": -1,
  "message": "Unauthorized: Invalid or revoked token"
}
Inactive/Invalidated Token
{
  "retcode": -1,
  "message": "Votre token est expiré, connectez-vous à votre compte pour le renouveler"
}
Archived Account
{
  "retcode": -1,
  "message": "Votre compte est désactivé, contactez Workways"
}
Archived User Account
{
  "retcode": -1,
  "message": "Unauthorized: User account is archived"
}
Security Best Practices:
  • Never commit API tokens to version control
  • Use environment variables to store tokens
  • Rotate tokens periodically
  • Revoke tokens immediately if compromised
  • Use different tokens for different applications/environments
  • Monitor token usage via the "Last Used" timestamp in your account settings

Response Format

All responses follow a standard JSON format:

{
  "retcode": 1,          // 1 for success, -1 for error
  "message": "OK",       // Status message
  "data": [...]          // Response data (array)
}

Country Input Flexibility

Country parameters accept multiple formats:
  • ISO codes (case-insensitive): "FR", "fr", "BE", "ie"
  • English names (case-insensitive): "France", "belgium", "IRELAND"
  • French names (case-insensitive): "France", "belgique", "IRLANDE"

All formats are normalized internally to ISO codes for database queries.

Country Output Format

All responses return English country names (e.g., "Ireland", "Belgium", "France") for user-friendly display, even though ISO codes are stored internally.

Language Support

All endpoints support the language parameter:
  • For GET requests: add ?language=fr or &language=fr to the URL
  • For POST requests: include "language": "fr" in the request body

When language=fr, error and success messages are returned in French. Default is English.

Only "fr" and "en" are supported. Any other value defaults to English.

Endpoints

GET /api/sites
Get a list of all public Workways sites with their available rooms.
⚡ Authentication Optional: This endpoint can be called with or without an API key.
  • Without API key: Returns only public rooms (publicOffice, publicMeeting)
  • With API key: Returns all rooms including private ones, with additional fields (actualState, otherRooms array)

Query Parameters

Parameter Type Required Description
country string Optional Filter sites by country. Accepts ISO codes ("IE", "BE", "FR"), English names ("Ireland", "Belgium"), or French names ("Irlande", "Belgique"). Case-insensitive.
type string Optional Filter by room type. Values: publicOffice, publicMeeting (without auth), or all plus any room type when authenticated. Use type=all with API key to get all room types.

Response Fields

Field Type Description
_id string Unique site identifier
name string Site name
country string Country where the site is located (English name, e.g., "Ireland", "Belgium")
publicAddress string | null Public address of the site, or null if not set
publicContact string | null Public contact information for the site, or null if not set
mapLink string | null URL to the site location on Google Maps or similar service, or null if not set
publicRooms array Array of public rooms available at this site
publicRooms[]._id string Room unique identifier
publicRooms[].name string Room internal name
publicRooms[].publicName string Room display name (falls back to name if not set)
publicRooms[].type string Room type: "publicOffice" or "publicMeeting"
publicRooms[].surface number Room surface in m² (commercial or computed), or -1 if unknown
publicRooms[].actualState string (Auth only) Current room state: "bookable", "rented", etc.
otherRooms array (Auth only) Array of non-public rooms (same structure as publicRooms)

Example Requests

# Using English name
curl -X GET "https://apidev.workways.com/api/sites?country=Ireland"

# Using ISO code
curl -X GET "https://apidev.workways.com/api/sites?country=IE"

# Using French name
curl -X GET "https://apidev.workways.com/api/sites?country=Irlande"

# Case insensitive
curl -X GET "https://apidev.workways.com/api/sites?country=ireland"

Example Response

{
  "retcode": 1,
  "message": "OK",
  "data": [
    {
      "_id": "507f1f77bcf86cd799439011",
      "name": "Dublin Coworking Center",
      "country": "Ireland",
      "publicAddress": "123 Main Street, Dublin 2, Ireland",
      "publicContact": "info@dublin-coworking.ie",
      "mapLink": "https://maps.google.com/?q=53.3498,-6.2603",
      "roomCount": 1,
      "meetingRoomCount": 1,
      "publicRooms": [
        {
          "_id": "507f191e810c19729de860ea",
          "name": "Meeting Room A",
          "publicName": "Meeting Room A",
          "type": "publicMeeting",
          "surface": 25
        },
        {
          "_id": "507f191e810c19729de860eb",
          "name": "Hot Desk 1",
          "publicName": "Hot Desk Area",
          "type": "publicOffice",
          "surface": -1
        }
      ]
    }
  ]
}

Example Response (Authenticated with type=all)

{
  "retcode": 1,
  "message": "OK",
  "data": [
    {
      "_id": "507f1f77bcf86cd799439011",
      "name": "Dublin Coworking Center",
      "country": "Ireland",
      "publicAddress": "123 Main Street, Dublin 2, Ireland",
      "publicContact": "info@dublin-coworking.ie",
      "mapLink": "https://maps.google.com/?q=53.3498,-6.2603",
      "roomCount": 1,
      "meetingRoomCount": 1,
      "publicRooms": [
        {
          "_id": "507f191e810c19729de860ea",
          "name": "Meeting Room A",
          "publicName": "Meeting Room A",
          "type": "publicMeeting",
          "surface": 25,
          "actualState": "bookable"
        }
      ],
      "otherRooms": [
        {
          "_id": "507f191e810c19729de860ec",
          "name": "Private Office 101",
          "publicName": "Executive Suite",
          "type": "privateOffice",
          "surface": 45,
          "actualState": "rented"
        }
      ]
    }
  ]
}
🚀 Try it now!
Leave empty for all countries
Response:

            
GET /api/rooms
Get a detailed list of all public rooms with pricing, capacity, and availability information.
⚡ Authentication Optional: This endpoint can be called with or without an API key.
  • Without API key: Returns only public rooms (publicOffice, publicMeeting)
  • With API key: Returns all room types, plus organization info when applicable (organizationId, organizationName, contractEndDate)

Query Parameters

Parameter Type Required Description
country string Optional Filter rooms by country. Accepts ISO codes ("IE", "BE"), English names ("Ireland", "Belgium"), or French names ("Irlande", "Belgique"). Case-insensitive.
site string Optional Filter rooms by site ID (ObjectId)

Response Fields

Field Type Description
_id string Unique room identifier
name string Room internal name
publicName string Room display name (falls back to name if not set)
type string Room type: "publicOffice", "publicMeeting", or other types when authenticated
surface number Room surface in m² (commercial or computed), or -1 if unknown
description string Room description
actualState string Current state: "bookable", "rented", "forrent", etc.
siteId string ID of the site where this room is located
siteName string Name of the site
country string Country of the site (English name, e.g., "Ireland", "Belgium")
minPerson number Minimum number of persons
maxPerson number Maximum number of persons
nbSeat number Number of seats
configuration string Room configuration type
otherConfiguration string Additional configuration details
minDurationBooking number Minimum booking duration (minutes)
maxDurationBooking number Maximum booking duration (minutes)
stepDurationBooking number Booking duration increment (minutes)
slotWeek string Weekly availability schedule
slotSaturday string Saturday availability schedule
slotSunday string Sunday availability schedule
pricePerHour number Price per hour (€). Returns 0 if no API key provided.
pricePerHalfDay number Price per half day (€). Returns 0 if no API key provided.
pricePerDay number Price per day (€). Returns 0 if no API key provided.
pricePerMonth string Monthly rental price
image string (URL) Room image URL (via /imageroom endpoint)
publicDescriptionEn string | null Public description of the room in English, or null if not set
publicDescriptionFr string | null Public description of the room in French, or null if not set
organizationId string (Auth only) Organization ID if room is rented
organizationName string (Auth only) Organization name if room is rented
contractEndDate string (Auth only) Contract end date in YYYY-MM-DD format (from room or organization), or empty string if not set

Example Requests

# Filter by English country name
curl -X GET "https://apidev.workways.com/api/rooms?country=Ireland"

# Filter by ISO code
curl -X GET "https://apidev.workways.com/api/rooms?country=FR"

# Filter by specific site
curl -X GET "https://apidev.workways.com/api/rooms?site=507f1f77bcf86cd799439011"

# Combine filters (French name + site)
curl -X GET "https://apidev.workways.com/api/rooms?country=belgique&site=507f1f77bcf86cd799439011"

Example Response

{
  "retcode": 1,
  "message": "OK",
  "data": [
    {
      "_id": "507f191e810c19729de860ea",
      "name": "Meeting Room A",
      "publicName": "Meeting Room A",
      "type": "publicMeeting",
      "description": "Modern meeting room with video conferencing",
      "actualState": "bookable",
      "surface": 25,
      "siteId": "507f1f77bcf86cd799439011",
      "siteName": "Dublin Coworking Center",
      "country": "Ireland",
      "minPerson": 2,
      "maxPerson": 8,
      "nbSeat": 8,
      "configuration": "boardroom",
      "otherConfiguration": "Video conferencing, whiteboard",
      "minDurationBooking": 60,
      "maxDurationBooking": 480,
      "stepDurationBooking": 30,
      "slotWeek": "09:00-18:00",
      "slotSaturday": "",
      "slotSunday": "",
      "pricePerHour": 0,
      "pricePerHalfDay": 0,
      "pricePerDay": 0,
      "pricePerMonth": "500",
      "image": "https://apidev.workways.com/imageroom?id=imageId123",
      "publicDescriptionEn": "A bright meeting room with natural light and modern equipment.",
      "publicDescriptionFr": "Une salle de réunion lumineuse avec éclairage naturel et équipement moderne."
    }
  ]
}

Example Response (Authenticated - Private Room with Organization)

{
  "retcode": 1,
  "message": "OK",
  "data": [
    {
      "_id": "507f191e810c19729de860ec",
      "name": "Private Office 101",
      "publicName": "Executive Suite",
      "type": "privateOffice",
      "description": "Fully equipped private office",
      "actualState": "rented",
      "surface": 45,
      "siteId": "507f1f77bcf86cd799439011",
      "siteName": "Dublin Coworking Center",
      "country": "Ireland",
      "minPerson": 1,
      "maxPerson": 4,
      "nbSeat": 4,
      "configuration": "office",
      "otherConfiguration": "",
      "minDurationBooking": 0,
      "maxDurationBooking": 0,
      "stepDurationBooking": 0,
      "slotWeek": "",
      "slotSaturday": "",
      "slotSunday": "",
      "pricePerHour": 0,
      "pricePerHalfDay": 0,
      "pricePerDay": 0,
      "pricePerMonth": "1500",
      "image": "https://apidev.workways.com/imageroom?id=imageId456",
      "publicDescriptionEn": "Spacious private office with panoramic views.",
      "publicDescriptionFr": "Bureau privé spacieux avec vue panoramique.",
      "organizationId": "507f1f77bcf86cd799439099",
      "organizationName": "Acme Corp",
      "contractEndDate": "2025-11-30"
    }
  ]
}
🚀 Try it now!
Leave empty for all countries Leave empty for all sites
Response:

            
GET /api/countries
Get a list of all countries where Workways has public sites. This endpoint is useful for populating country dropdowns and filters.

Query Parameters

This endpoint does not accept any query parameters.

Response Fields

Field Type Description
retcode number 1 for success, -1 for error
message string Response message ("OK" or error message)
data array of strings Alphabetically sorted list of country names (English)

Example Request

curl -X GET "https://apidev.workways.com/api/countries"

Example Response

{
  "retcode": 1,
  "message": "OK",
  "data": [
    "Belgium",
    "France",
    "Ireland"
  ]
}
🚀 Try it now!
Response:

            
GET /api/icsfeed
Get an ICS calendar feed for one or more meeting rooms. This endpoint returns an iCalendar file (.ics) that can be imported into Google Calendar, Outlook, Apple Calendar, and other calendar applications.

Query Parameters

Parameter Type Required Description
rooms string Required Comma-separated list of room IDs (MongoDB ObjectId format). Example: "507f191e810c19729de860ea,507f1f77bcf86cd799439011"

Response Format

Returns an .ics file (iCalendar format) containing all meetings for the specified rooms.

Example Requests

# Single room
curl -X GET "https://apidev.workways.com/api/icsfeed?rooms=507f191e810c19729de860ea"

# Multiple rooms
curl -X GET "https://apidev.workways.com/api/icsfeed?rooms=507f191e810c19729de860ea,507f1f77bcf86cd799439011"

# Download to file
curl -X GET "https://apidev.workways.com/api/icsfeed?rooms=507f191e810c19729de860ea" -o calendar.ics

Example Response

BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Workways//Meeting Calendar//EN
CALSCALE:GREGORIAN
BEGIN:VEVENT
UID:meeting123@workways.com
DTSTART:20251030T140000Z
DTEND:20251030T150000Z
SUMMARY:Team Meeting
LOCATION:Meeting Room A
DESCRIPTION:Weekly team sync
END:VEVENT
END:VCALENDAR

Calendar Integration

How to use with calendar applications:
  • Google Calendar: Go to Settings → Add calendar → From URL → Paste the ICS feed URL
  • Outlook: File → Account Settings → Internet Calendars → New → Paste URL
  • Apple Calendar: File → New Calendar Subscription → Paste URL
  • Direct download: Open the URL in a browser to download the .ics file

Error Response

{
  "retcode": -1,
  "message": "Bad request: \"rooms\" query parameter is missing, empty, or contains invalid IDs."
}
POST /api/register
Register a new organization (company) for access to Workways spaces. This is the first step for companies wanting to book meeting rooms or offices.

Request Body

Field Type Required Description
name string Required Company name
firstName string Required Contact first name
lastName string Optional Contact last name
email string Required Contact email (used for billing and notifications)
phone string Required Contact phone number (with country code, e.g., +33612345678)
country string Optional Country code: "fr" (France), "be" (Belgium), or "ie" (Ireland). If not provided, derived from the site.
maxResidentUsers number Required Number of users with site access (minimum: 1)
companyNumber string Optional SIRET (France), KBO (Belgium), or VAT number (Ireland)
address string Optional Company billing address
vatNumber string Optional VAT number (if applicable)
language string Optional Language for API messages and new user/organization: "fr" or "en" (default: "en"). Error and success messages will be in the specified language.
siteId string Optional ID of the site to link with this registration (MongoDB ObjectId). Highest priority for site determination.
roomId string Optional ID of a room of interest (MongoDB ObjectId). Used to determine the site if siteId is not provided.
meetingId string Optional ID of a pending meeting request to link with this registration (MongoDB ObjectId). Used to determine the site if neither siteId nor roomId are provided.
Site Determination Priority:
  • At least one of siteId, roomId, or meetingId must be provided
  • Priority order: siteIdroomIdmeetingId
  • If multiple parameters point to different sites, all sites are added to the organization's memberOf array in priority order
  • The country is derived from the highest priority site if not explicitly provided

Example Request

curl -X POST "https://apidev.workways.com/api/register" \
  -H "Authorization: Bearer ww_dev_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Acme Corp",
    "firstName": "Jean",
    "lastName": "Dupont",
    "email": "jean.dupont@acme.com",
    "phone": "+33612345678",
    "maxResidentUsers": 5,
    "companyNumber": "12345678901234",
    "address": "123 rue de la Paix, 75001 Paris, France",
    "vatNumber": "FR12345678901",
    "language": "fr",
    "siteId": "507f1f77bcf86cd799439011",
    "roomId": "507f191e810c19729de860ea"
  }'

Response Scenarios

The response depends on whether the user/organization already exists:

Scenario 1: New Organization Created (CREATED)
{
  "retcode": 200,
  "status": "CREATED",
  "message": "Organization registered successfully. A verification email has been sent.",
  "data": {
    "userId": "507f1f77bcf86cd799439011",
    "userEmail": "jean.dupont@acme.com",
    "userRole": "principal-admin",
    "userCreated": true,
    "organizationId": "507f191e810c19729de860ea",
    "organizationName": "Acme Corp",
    "organizationCreated": true,
    "meetingsUpdated": 1,
    "roomOfInterest": {
      "_id": "507f1f77bcf86cd799439022",
      "name": "Office Premium",
      "siteName": "Workways Paris",
      "siteId": "507f1f77bcf86cd799439033"
    },
    "identityVerification": {
      "source": "insee_siret",
      "confidence": 100,
      "verifiedName": "ACME CORPORATION",
      "verifiedNumber": "12345678901234"
    }
  }
}
Scenario 2: Existing Organization Updated (UPDATED)
{
  "retcode": 200,
  "status": "UPDATED",
  "message": "Registration successful. Your organization has been updated.",
  "data": {
    "userId": "507f1f77bcf86cd799439011",
    "userEmail": "jean.dupont@acme.com",
    "userRole": "principal-admin",
    "userCreated": false,
    "organizationId": "507f191e810c19729de860ea",
    "organizationName": "Acme Corp",
    "organizationCreated": false,
    "meetingsUpdated": 0,
    "roomOfInterest": null,
    "identityVerification": null
  }
}
Scenario 3: User Already Exists with Organization (USER_EXISTS)
{
  "retcode": 200,
  "status": "USER_EXISTS",
  "message": "User already registered with an organization.",
  "data": {
    "userId": "507f1f77bcf86cd799439011",
    "organizationId": "507f191e810c19729de860ea"
  }
}

Response Fields

Field Type Description
retcode number 200 = success, -1 = error
status string CREATED, UPDATED, or USER_EXISTS
message string Human-readable status message
data.organizationId string Always present. The organization ID (existing or newly created). Use this to link further operations.
data.userId string The user ID (existing or newly created)
data.userEmail string The user's email address (CREATED/UPDATED only)
data.userRole string The user's role (typically "principal-admin") (CREATED/UPDATED only)
data.userCreated boolean True if a new user was created, false if existing user was updated (CREATED/UPDATED only)
data.organizationName string The organization's name (CREATED/UPDATED only)
data.organizationCreated boolean True if a new organization was created, false if existing one was used (CREATED/UPDATED only)
data.meetingsUpdated number Number of pending meeting requests linked to the organization (CREATED/UPDATED only)
data.roomOfInterest object|null Information about the room if roomId was provided (CREATED/UPDATED only)
data.identityVerification object|null Company identity verification result if companyNumber was provided (CREATED/UPDATED only)
Important: The organizationId field is always returned in all success scenarios (CREATED, UPDATED, USER_EXISTS). You should store this ID to reference the organization in subsequent API calls or for your own records.

Process Flow

What happens after registration:
  1. You receive a confirmation email with a validation link
  2. Click the link to verify your email and set your password
  3. Your company number (SIRET/KBO/VAT) is verified automatically (if provided)
  4. Once validated, you can access the Workways platform
  5. Your access cards are activated
POST /api/meeting-request
Request a meeting room booking. The behavior depends on whether your email is already registered in the system.

Important Notes

  • Existing users: Bookings are confirmed immediately (status: ACCEPTED)
  • New users: Must complete registration first (status: REQUEST_ACCEPTED)
  • Minimum advance for new users: Bookings must be at least 2 days in advance
  • Existing users: Can book same-day (but not in the past)
  • Archived accounts: Cannot book (status: ARCHIVED)
  • Public rooms only: Only rooms with type "publicOffice" or "publicMeeting" can be booked
  • Room availability: The requested time slot must be free. If another meeting is already booked during this period, you will receive a ROOM_OVERLAP error (HTTP 409)

Request Body

Field Type Required Description
contactEmail string Required Contact email for this booking
contactName string Optional Contact name
bookedRoom string Required Room ID (MongoDB ObjectId)
bookedAt string Required Start date/time (ISO 8601 format)
duration number Required Duration in minutes (minimum: 30)
bookedUser number Optional Number of attendees (default: 1)
name string Optional Meeting subject/title
description string Optional Meeting description or notes
language string Optional Language for API messages and new user: "fr" or "en" (default: "en"). Error and success messages will be in the specified language.

Example Request

curl -X POST "https://apidev.workways.com/api/meeting-request" \
  -H "Authorization: Bearer ww_dev_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{
    "contactEmail": "jean.dupont@acme.com",
    "contactName": "Jean Dupont",
    "bookedRoom": "507f191e810c19729de860ea",
    "bookedAt": "2025-11-05T14:00:00Z",
    "duration": 60,
    "bookedUser": 4,
    "name": "Quarterly Review",
    "description": "Q4 2025 team meeting"
  }'

Response Scenarios

Scenario 1: Existing User - Booking Accepted
{
  "retcode": 200,
  "status": "ACCEPTED",
  "message": "Your booking has been confirmed.",
  "data": {
    "meetingId": "507f191e810c19729de860ea",
    "roomName": "Meeting Room A",
    "bookedDate": "2025-11-05",
    "bookedTime": "14:00-15:00",
    "duration": 60
  }
}
Scenario 2: New User - Registration Required
{
  "retcode": 200,
  "status": "REQUEST_ACCEPTED",
  "message": "Your meeting request has been received. Please complete your registration to confirm the booking.",
  "data": {
    "meetingRequestId": "507f191e810c19729de860ea",
    "userId": "507f191e810c19729de860eb",
    "roomName": "Meeting Room A",
    "requestedDate": "2025-11-05",
    "requestedTime": "14:00-15:00",
    "duration": 60
  }
}
Linking Meeting to Registration: When you receive a REQUEST_ACCEPTED response, save the meetingRequestId. You can then pass it as the meetingId parameter when calling /api/register to automatically link the meeting to the new organization once registration is complete.
Scenario 3: Archived User Account
{
  "retcode": 200,
  "status": "ARCHIVED",
  "message": "Your account is no longer valid. Please contact an administrator to restore it.",
  "data": null
}

Error Response Examples

// New user: same-day or past date booking attempt
{
  "retcode": -1,
  "message": "Cannot book meetings for today or past dates"
}

// New user: insufficient advance notice
{
  "retcode": -1,
  "message": "Meetings must be booked at least 2 days in advance"
}

// Existing user: past date booking attempt
{
  "retcode": -1,
  "message": "Cannot book meetings for past dates"
}

// Room not public
{
  "retcode": -1,
  "message": "This room is not available for public booking"
}

// Invalid room ID
{
  "retcode": -1,
  "message": "Room not found"
}

// Room time slot not available (HTTP 409)
{
  "retcode": -1,
  "status": "ROOM_OVERLAP",
  "message": "This time slot is not available. Another meeting is already booked during this period."
}
GET /api/room-price

Description: Calculate the booking price for a room based on room type, booking parameters, and room state.

🔒 Authentication Required: This endpoint requires an API token. Include it in the Authorization header:
Authorization: Bearer ww_dev_your_token_here

Supported Room Types

Meeting Rooms (hourly/daily pricing):
  • publicMeeting - No state restrictions
  • meeting, privateMeeting, sharedMeeting, conference - Requires defaultState to be bookable or occupied
Office Rooms (weekly/monthly pricing):
  • publicOffice - No state restrictions
  • office, privateOffice, sharedOffice - Requires defaultState to be rented or forrent

Query Parameters

For Meeting Rooms:
Parameter Type Required Description
roomId string ✅ Yes MongoDB ObjectId of the room
startDate string ⚠️ Optional Start date/time in flexible format (e.g., "2025-01-18T10:00:00")
duration number ❌ No Duration in minutes (default: 60)
nbPersons number ❌ No Number of persons (default: 2)
For Office Rooms:
Parameter Type Required Description
roomId string ✅ Yes MongoDB ObjectId of the room
period string ❌ No Rental period: "week" or "month" (default: "month")

Example Requests

Meeting Room Price
curl -H "Authorization: Bearer ww_dev_your_token_here" \
  "https://api.workways.com/api/room-price?roomId=507f1f77bcf86cd799439011&startDate=2025-01-18T10:00:00&duration=120&nbPersons=4"
Office Room Price
curl -H "Authorization: Bearer ww_dev_your_token_here" \
  "https://api.workways.com/api/room-price?roomId=507f1f77bcf86cd799439011&period=month"

Success Response Examples

Meeting Room - Price Available
{
  "retcode": 1,
  "message": "OK",
  "data": {
    "roomId": "507f1f77bcf86cd799439011",
    "roomType": "privateMeeting",
    "defaultState": "bookable",
    "actualState": "free",
    "startDate": "2025-01-18T10:00:00",
    "duration": 120,
    "nbPersons": 4,
    "price": 100
  }
}
Office Room - Price Available
{
  "retcode": 1,
  "message": "OK",
  "data": {
    "roomId": "507f1f77bcf86cd799439011",
    "roomType": "privateOffice",
    "defaultState": "rented",
    "actualState": "rented",
    "period": "month",
    "price": 100
  }
}
Room State Not Supported - Price Unavailable
{
  "retcode": -1,
  "message": "Price calculation not available for this room state/type",
  "data": {
    "roomId": "507f1f77bcf86cd799439011",
    "roomType": "meeting",
    "defaultState": "toclean",
    "actualState": "toclean",
    "price": -1
  }
}
⚠️ Price = -1: When price is -1, the price calculation is not available for the room's current state or type. The endpoint still returns 200 OK to allow graceful handling on the client side.

Error Response Examples

// Missing roomId
{
  "retcode": -1,
  "message": "Missing required parameter: roomId"
}

// Invalid roomId format
{
  "retcode": -1,
  "message": "Invalid roomId format"
}

// Room not found
{
  "retcode": -1,
  "message": "Room not found"
}

// Unsupported room type
{
  "retcode": -1,
  "message": "Price calculation not supported for room type: privateAccess"
}

// Invalid period (for offices)
{
  "retcode": -1,
  "message": "Invalid period: must be \"week\" or \"month\""
}

// Invalid duration (for meetings)
{
  "retcode": -1,
  "message": "Invalid duration: must be a positive number"
}

// Unauthorized (no token)
{
  "retcode": -1,
  "message": "Unauthorized: Authorization header is required. Use: Authorization: Bearer "
}

Response Fields

Field Type Description
retcode number 1 = success, -1 = error or price unavailable
message string Human-readable status message
data.roomId string ID of the room
data.roomType string Type of the room (e.g., "publicMeeting", "privateOffice")
data.defaultState string Room's default state (e.g., "bookable", "rented")
data.actualState string Room's current state (e.g., "free", "occupied")
data.price number Calculated price, or -1 if unavailable
data.startDate string (Meetings only) Start date/time if provided
data.duration number (Meetings only) Duration in minutes
data.nbPersons number (Meetings only) Number of persons
data.period string (Offices only) Rental period ("week" or "month")

GET /api/contract-options 🔐 Auth Required

Returns available contract options (additional services) for a specific site. Options are based on Rate records and can include parking, equipment rentals, monthly services, etc.

Query Parameters

Parameter Type Required Description
site string Yes Site ID to filter available options

Example Request

curl -H "Authorization: Bearer ww_dev_your_token_here" \
  "https://api.workways.com/api/contract-options?site=507f1f77bcf86cd799439011"

Success Response Example

{
  "retcode": 1,
  "message": "OK",
  "data": [
    {
      "_id": "507f1f77bcf86cd799439011",
      "key": "parking",
      "name": "Parking space",
      "nameFr": "Place de Parking",
      "nameEn": "Parking Space",
      "type": "other",
      "isUnique": true,
      "isGeneric": false,
      "supportsQuantity": false,
      "pricing": {
        "publicBase": 150,
        "memberBase": 135,
        "publicPromo": null,
        "memberPromo": null,
        "promoMonthDuration": null,
        "setupFees": 50
      },
      "vat": {
        "type": "standard",
        "rate": null
      }
    },
    {
      "_id": "507f1f77bcf86cd799439022",
      "key": "tv_rental",
      "name": "TV Rental",
      "nameFr": "Location TV",
      "nameEn": "TV Rental",
      "type": "quantity",
      "isUnique": false,
      "isGeneric": true,
      "supportsQuantity": true,
      "pricing": {
        "publicBase": 30,
        "memberBase": 27,
        "publicPromo": null,
        "memberPromo": null,
        "promoMonthDuration": null,
        "setupFees": 0
      },
      "vat": {
        "type": "standard",
        "rate": null
      }
    },
    {
      "_id": "507f1f77bcf86cd799439033",
      "key": "service_by_month",
      "name": "Monthly Service",
      "nameFr": "Service mensuel",
      "nameEn": "Monthly Service",
      "type": "promo",
      "isUnique": false,
      "isGeneric": true,
      "supportsQuantity": false,
      "pricing": {
        "publicBase": 200,
        "memberBase": 180,
        "publicPromo": 150,
        "memberPromo": 135,
        "promoMonthDuration": 3,
        "setupFees": 100
      },
      "vat": {
        "type": "special",
        "rate": 10
      }
    }
  ]
}

Option Types

Category Keys Description
Unique (one per organization) parking, bike_garage, meeting_room_package, food_service, garbage_service, cleaning_service, private_network, private_wifi Can only be subscribed once per organization
Generic (multiple allowed) tv_rental, fridge_rental, phonebooth_rental, appliance_rental, furniture_rental, service_by_month, service_by_month_prepaid Can be subscribed multiple times per organization

Rate Types

Type Description Features
other Standard service Fixed pricing
quote Quote-based service Price determined on request
quantity Quantity-based service Price multiplied by quantity
promo Promotional pricing Discounted price for initial period
promoWithQuantity Promotional + quantity Both promotional pricing and quantity support

Error Response Examples

// Missing site parameter
{
  "retcode": -1,
  "message": "Missing required parameter: site"
}

// Invalid site ID format
{
  "retcode": -1,
  "message": "Invalid site ID format"
}

// Site not found
{
  "retcode": -1,
  "message": "Site not found"
}

// Unauthorized (no token)
{
  "retcode": -1,
  "message": "Unauthorized: Authorization header is required. Use: Authorization: Bearer <your_token>"
}

Response Fields

Field Type Description
_id string Unique rate ID
key string Option identifier key
name string Technical name
nameFr string Translated name in French
nameEn string Translated name in English
type string Rate type (other, quote, quantity, promo, promoWithQuantity)
isUnique boolean True if only one subscription allowed per organization
isGeneric boolean True if multiple subscriptions allowed
supportsQuantity boolean True if the option supports quantity (type = quantity or promoWithQuantity)
pricing.publicBase number Public base price (monthly)
pricing.memberBase number Member base price (monthly)
pricing.publicPromo number|null Public promotional price (if applicable)
pricing.memberPromo number|null Member promotional price (if applicable)
pricing.promoMonthDuration number|null Promotional period duration in months
pricing.setupFees number One-time setup/installation fees
vat.type string VAT type: "standard", "none", or "special"
vat.rate number|null Custom VAT rate (only if type = "special")
💡 Pricing Logic:
  • Member vs Public: Organizations with member status use memberBase/memberPromo prices; others use publicBase/publicPromo
  • Promotional Period: For promo types, the promo price applies for the first N months (defined by promoMonthDuration), then switches to base price
  • Quantity: For quantity-based options, the total price = unit price × quantity

GET /api/user-info 🔐 Auth Required

Returns user information by ID, email address, or a paginated list. Supports filtering by organization. Useful for HubSpot synchronization and CRM integration.

Query Parameters

Parameter Type Required Description
id string No User ID (MongoDB ObjectId). Returns single user. Takes priority over email.
email string No User email address (case insensitive). Returns single user.
organization string No Filter users by organization ID (for list mode only)
page number No Page number (1-based, default: 1). For list mode only.
limit number No Items per page (default: 50, max: 200). For list mode only.

Note: If neither id nor email is provided, returns a paginated list of all users.

Example Requests

Single User by ID
curl -H "Authorization: Bearer ww_dev_your_token_here" \
  "https://api.workways.com/api/user-info?id=507f1f77bcf86cd799439011"
Single User by Email
curl -H "Authorization: Bearer ww_dev_your_token_here" \
  "https://api.workways.com/api/user-info?email=jean.dupont@acme.com"
Paginated List (all users)
curl -H "Authorization: Bearer ww_dev_your_token_here" \
  "https://api.workways.com/api/user-info?page=1&limit=50"
Users from a Specific Organization
curl -H "Authorization: Bearer ww_dev_your_token_here" \
  "https://api.workways.com/api/user-info?organization=507f191e810c19729de860ea&page=1"

Success Response - Single User

{
  "retcode": 1,
  "message": "OK",
  "data": {
    "_id": "507f1f77bcf86cd799439011",
    "firstName": "Jean",
    "lastName": "Dupont",
    "email": "jean.dupont@acme.com",
    "title": "Directeur Commercial",
    "language": "fr",
    "phone": "+33 1 23 45 67 89",
    "mobilePhone": "+33 6 12 34 56 78",
    "role": "principal-admin",
    "newsOptin": true,
    "mailValid": true,
    "isUserArchived": false,
    "organizationId": "507f191e810c19729de860ea",
    "organizationName": "Acme Corp",
    "isOrganizationArchived": false,
    "updatedAt": "2025-01-15"
  }
}

Success Response - Paginated List

{
  "retcode": 1,
  "message": "OK",
  "data": {
    "items": [
      {
        "_id": "507f1f77bcf86cd799439011",
        "firstName": "Jean",
        "lastName": "Dupont",
        "email": "jean.dupont@acme.com",
        "title": "Directeur Commercial",
        "language": "fr",
        "phone": "+33 1 23 45 67 89",
        "mobilePhone": "+33 6 12 34 56 78",
        "role": "principal-admin",
        "newsOptin": true,
        "mailValid": true,
        "isUserArchived": false,
        "organizationId": "507f191e810c19729de860ea",
        "organizationName": "Acme Corp",
        "isOrganizationArchived": false,
        "updatedAt": "2025-01-15"
      },
      {
        "_id": "507f1f77bcf86cd799439012",
        "firstName": "Marie",
        "lastName": "Martin",
        "email": "marie.martin@example.com",
        "title": null,
        "language": "en",
        "phone": null,
        "mobilePhone": null,
        "role": "user",
        "newsOptin": false,
        "mailValid": true,
        "isUserArchived": false,
        "organizationId": null,
        "organizationName": null,
        "isOrganizationArchived": null,
        "updatedAt": "2025-01-10"
      }
    ],
    "total": 1250,
    "page": 1,
    "limit": 50,
    "pages": 25
  }
}

Error Response Examples

// Invalid user ID format
{
  "retcode": -1,
  "message": "Invalid user ID format"
}

// Invalid email format
{
  "retcode": -1,
  "message": "Invalid email format"
}

// Invalid organization ID format
{
  "retcode": -1,
  "message": "Invalid organization ID format"
}

// User not found
{
  "retcode": -1,
  "message": "User not found"
}

// Unauthorized (no token)
{
  "retcode": -1,
  "message": "Unauthorized: Authorization header is required. Use: Authorization: Bearer <your_token>"
}

Response Fields

Field Type Description
_id string Unique user ID
firstName string User's first name
lastName string User's last name (may be empty)
email string User's email address
title string|null User's job title (e.g., "Directeur Commercial")
language string|null User's preferred language (fr, en, nl...)
phone string|null Office phone number
mobilePhone string|null Mobile phone number
role string User's role (e.g., "principal-admin", "admin", "user", "temp")
newsOptin boolean True if user has opted in to newsletter
mailValid boolean True if email has been verified
isUserArchived boolean True if the user account is archived
organizationId string|null ID of the user's organization (null if not associated)
organizationName string|null Name of the user's organization (null if not associated)
isOrganizationArchived boolean|null True if the organization is archived (null if no organization)
updatedAt string|null Last modification date (YYYY-MM-DD format)

Pagination Fields (List Mode)

Field Type Description
items array Array of user objects
total number Total number of users matching the query
page number Current page number (1-based)
limit number Items per page
pages number Total number of pages

User Roles

Role Description
principal-admin Primary administrator of an organization (billing contact)
admin Administrator with management privileges
user Standard user with access privileges
temp Temporary user (pending registration completion)

POST /api/set-optin 🔐 Auth Required

Updates a user's newsletter optin preference. Can also be called via GET for convenience.

Parameters (Query or Body)

Parameter Type Required Description
user string Yes User ID (MongoDB ObjectId)
optin boolean Yes Newsletter optin value. Accepts: true/false, 1/0, "true"/"false"

Example Requests

// Via query parameters
curl -X POST \
  -H "Authorization: Bearer ww_dev_xxxx" \
  "https://api.workways.com/api/set-optin?user=507f1f77bcf86cd799439011&optin=true"
// Via request body
curl -X POST \
  -H "Authorization: Bearer ww_dev_xxxx" \
  -H "Content-Type: application/json" \
  -d '{"user": "507f1f77bcf86cd799439011", "optin": true}' \
  "https://api.workways.com/api/set-optin"

Success Response

{
  "retcode": 1,
  "message": "OK",
  "data": {
    "userId": "507f1f77bcf86cd799439011",
    "email": "jean.dupont@acme.com",
    "newsOptin": true,
    "previousValue": false
  }
}

Error Response Examples

// Missing user parameter
{
  "retcode": -1,
  "message": "Missing required parameter: user (user ID)"
}

// Invalid user ID format
{
  "retcode": -1,
  "message": "Invalid user ID format"
}

// Missing optin parameter
{
  "retcode": -1,
  "message": "Missing required parameter: optin (true/false)"
}

// Invalid optin value
{
  "retcode": -1,
  "message": "Invalid optin value. Use true/false, 1/0, or \"true\"/\"false\""
}

// User not found
{
  "retcode": -1,
  "message": "User not found"
}

Response Fields

Field Type Description
userId string The updated user's ID
email string The user's email address
newsOptin boolean The new newsletter optin value
previousValue boolean The previous newsletter optin value

GET /api/organization-info 🔐 Auth Required

Returns organization information by ID or a paginated list. Includes contract details, principal site, billing information, and more. Useful for HubSpot synchronization and CRM integration.

Query Parameters

Parameter Type Required Description
id string No Organization ID (MongoDB ObjectId). Returns single organization with full details.
site string No Filter organizations by site ID (organizations that are members of this site). For list mode only.
page number No Page number (1-based, default: 1). For list mode only.
limit number No Items per page (default: 50, max: 200). For list mode only.

Note: If id is not provided, returns a paginated list of all organizations. Use site to filter by site membership. List mode excludes detailed arrays (allocatedRooms, accessibleSites, contractOptions) for performance.

Example Requests

Single Organization by ID (full details)
curl -H "Authorization: Bearer ww_dev_your_token_here" \
  "https://api.workways.com/api/organization-info?id=507f1f77bcf86cd799439011"
Paginated List (all organizations)
curl -H "Authorization: Bearer ww_dev_your_token_here" \
  "https://api.workways.com/api/organization-info?page=1&limit=50"
Organizations from a Specific Site
curl -H "Authorization: Bearer ww_dev_your_token_here" \
  "https://api.workways.com/api/organization-info?site=507f1f77bcf86cd799439022&page=1"

Success Response - Single Organization

{
  "retcode": 1,
  "message": "OK",
  "data": {
    "_id": "507f1f77bcf86cd799439011",
    "name": "Acme Corp",
    "description": "Technology solutions company",
    "language": "fr",
    "isArchived": false,
    "principalSiteId": "507f1f77bcf86cd799439022",
    "principalSiteName": "Workways Paris",
    "principalSiteCountry": "fr",
    "sitesCount": 2,
    "maxResidentUsers": 10,
    "isMember": true,
    "contractStartDate": "2024-01-01",
    "contractEndDate": "2025-12-31",
    "renewalMonths": 12,
    "basePrice": 1300,
    "lastAccessDate": "2025-01-27",
    "accountType": "customer",
    "billingName": "Acme Corp SAS",
    "billingEmail": "comptabilite@acme.com",
    "billingAddress": "123 Avenue des Champs-Elysees, 75008 Paris",
    "vatNumber": "FR12345678901",
    "companyNumber": "123456789",
    "contactName": "Jean Dupont",
    "contactPhone": "+33 1 23 45 67 89",
    "updatedAt": "2025-01-15",
    "allocatedRooms": [
      {
        "_id": "507f191e810c19729de860ea",
        "name": "Bureau Premium A12",
        "contractEndDate": "2025-12-31",
        "price": 850,
        "siteId": "507f1f77bcf86cd799439022"
      }
    ],
    "accessibleSites": [
      {
        "_id": "507f1f77bcf86cd799439022",
        "name": "Workways Paris",
        "country": "fr"
      },
      {
        "_id": "507f1f77bcf86cd799439033",
        "name": "Workways Lyon",
        "country": "fr"
      }
    ],
    "contractOptions": [
      {
        "id": 1,
        "name": "Place de parking",
        "key": "parking",
        "quantity": 2,
        "price": 150,
        "pricePromo": null,
        "startDate": "2024-01-01",
        "endDate": ""
      }
    ]
  }
}

Success Response - Paginated List

{
  "retcode": 1,
  "message": "OK",
  "data": {
    "items": [
      {
        "_id": "507f1f77bcf86cd799439011",
        "name": "Acme Corp",
        "description": "Technology solutions company",
        "language": "fr",
        "isArchived": false,
        "principalSiteId": "507f1f77bcf86cd799439022",
        "principalSiteName": "Workways Paris",
        "principalSiteCountry": "fr",
        "sitesCount": 2,
        "maxResidentUsers": 10,
        "isMember": true,
        "contractStartDate": "2024-01-01",
        "contractEndDate": "2025-12-31",
        "renewalMonths": 12,
        "basePrice": 1300,
        "lastAccessDate": "2025-01-27",
        "accountType": "customer",
        "billingName": "Acme Corp SAS",
        "billingEmail": "comptabilite@acme.com",
        "billingAddress": "123 Avenue des Champs-Elysees, 75008 Paris",
        "vatNumber": "FR12345678901",
        "companyNumber": "123456789",
        "contactName": "Jean Dupont",
        "contactPhone": "+33 1 23 45 67 89",
        "updatedAt": "2025-01-15"
      },
      {
        "_id": "507f1f77bcf86cd799439012",
        "name": "Beta Industries",
        "description": null,
        "language": "en",
        "isArchived": false,
        "principalSiteId": "507f1f77bcf86cd799439033",
        "principalSiteName": "Workways Brussels",
        "principalSiteCountry": "be",
        "sitesCount": 1,
        "maxResidentUsers": 5,
        "isMember": true,
        "contractStartDate": "2024-06-01",
        "contractEndDate": null,
        "renewalMonths": null,
        "basePrice": 800,
        "lastAccessDate": "2025-01-26",
        "accountType": "customer",
        "billingName": "Beta Industries SPRL",
        "billingEmail": "finance@beta.be",
        "billingAddress": "45 Rue de la Loi, 1040 Brussels",
        "vatNumber": "BE0123456789",
        "companyNumber": "0123.456.789",
        "contactName": "Marie Martin",
        "contactPhone": "+32 2 123 45 67",
        "updatedAt": "2025-01-10"
      }
    ],
    "total": 350,
    "page": 1,
    "limit": 50,
    "pages": 7
  }
}

Error Response Examples

// Invalid organization ID format
{
  "retcode": -1,
  "message": "Invalid organization ID format"
}

// Invalid site ID format
{
  "retcode": -1,
  "message": "Invalid site ID format"
}

// Organization not found
{
  "retcode": -1,
  "message": "Organization not found"
}

// Unauthorized (no token)
{
  "retcode": -1,
  "message": "Unauthorized: Authorization header is required. Use: Authorization: Bearer <your_token>"
}

Response Fields

Field Type Description
_id string Unique organization ID
name string Organization name
description string|null Organization description
language string|null Preferred language (fr, en, nl...)
isArchived boolean True if the organization is archived
principalSiteId string|null ID of the principal site (computed: site with most allocated rooms)
principalSiteName string|null Name of the principal site
principalSiteCountry string|null Country code of the principal site (fr, be, ie...)
sitesCount number Number of sites the organization has access to
maxResidentUsers number Maximum number of resident users allowed by contract
isMember boolean True if the organization is a coworking member
contractStartDate string|null Contract start date (YYYY-MM-DD format)
contractEndDate string|null Contract end date (YYYY-MM-DD format, null if no end date)
renewalMonths number|null Number of months for automatic contract renewal
basePrice number|null Base monthly rental price
lastAccessDate string|null Date of last building access (YYYY-MM-DD format)
accountType string|null Account type (none, issuer, customer, test)
billingName string|null Billing entity name
billingEmail string|null Billing email address
billingAddress string|null Billing address
vatNumber string|null VAT number (TVA intracommunautaire)
companyNumber string|null Company registration number (SIRET, KBO/BCE...)
contactName string|null Primary contact name
contactPhone string|null Primary contact phone number
updatedAt string|null Last modification date (YYYY-MM-DD format)

Additional Fields (Single Organization Mode Only)

The following fields are only included when fetching a single organization by ID:

Field Type Description
allocatedRooms array List of rooms allocated to this organization
allocatedRooms[]._id string Room ID
allocatedRooms[].name string Room name
allocatedRooms[].contractEndDate string Room-specific contract end date (YYYY-MM-DD)
allocatedRooms[].price number|null Monthly rental price for this room
allocatedRooms[].siteId string|null ID of the site where the room is located
accessibleSites array List of sites the organization has access to
accessibleSites[]._id string Site ID
accessibleSites[].name string Site name
accessibleSites[].country string Country code (e.g., "fr", "be", "ie")
contractOptions array List of subscribed contract options
contractOptions[].id number Option ID (unique within the organization)
contractOptions[].name string Option display name
contractOptions[].key string Option key (e.g., "parking", "tv_rental")
contractOptions[].quantity number Quantity subscribed
contractOptions[].price number|null Custom price (null = uses default rate price)
contractOptions[].pricePromo number|null Custom promotional price
contractOptions[].startDate string Option start date (YYYY-MM-DD)
contractOptions[].endDate string Option end date (YYYY-MM-DD)

Pagination Fields (List Mode)

Field Type Description
items array Array of organization objects
total number Total number of organizations
page number Current page number (1-based)
limit number Items per page
pages number Total number of pages

POST /api/merge-contract 🔐 Auth Required

Merge/update contract information for an organization. Allows updating rental details, contract options, and room allocations.

Body Parameters (JSON)

Parameter Type Required Description
organizationId string Yes Organization ID (MongoDB ObjectId)
rentalPrice string No Rental price (e.g., "1500€")
rentalComment string No Comment about the rental contract
rentalStartAt string No Contract start date (YYYY-MM-DD format)
rentalEndAt string No Contract end date (YYYY-MM-DD format)
rentalUrl string No URL to Google Drive folder containing client documents
rentalRenewal number No Number of months for automatic renewal
contractOptionsToAdd array No List of contract options to add (see format below)
contractOptionsToRemove array No List of contract option IDs to remove
contractOptions array No Replace all contract options with this list
rooms array No Replace all room allocations with this list of room IDs
roomsToAdd array No List of room IDs to add to the organization
roomsToRemove array No List of room IDs to remove from the organization

Contract Option Format

When adding contract options via contractOptionsToAdd or contractOptions, each option should have:

{
  "optionModelId": "507f1f77bcf86cd799439099", // Required - Option ID from /api/contract-options response
  "key": "parking",           // Option key (see available keys below)
  "name": "Place de parking", // Display name
  "description": "Parking P1", // Description (optional)
  "quantity": 2,              // Quantity subscribed
  "price": 150,               // Custom price (optional, -1 = use default rate)
  "pricePromo": -1,           // Custom promo price (optional, -1 = use default)
  "promoMonthDuration": 3,    // Number of months for promo price (optional)
  "startAt": "2024-01-01",    // Option start date (YYYY-MM-DD, optional)
  "endAt": "",                // Option end date (YYYY-MM-DD, optional, empty = inherits from org)
  "setupFees": 50,            // One-time setup fees (optional)
  "setupAt": "2024-01-01",    // Setup date (YYYY-MM-DD, optional)
  // For credit options only:
  "priceRemaining": 500,      // Remaining credit amount (for credit_fixed/credit_percent)
  "creditUsed": false         // Whether the credit has been fully used
}

Available Option Keys

Key Description (EN) Description (FR)
parkingParking spacePlace de parking
bike_garageSecure bike garageGarage vélo sécurisé
meeting_room_packageMeeting room booking packageForfait réservation de salles
tv_rentalTV rentalLocation téléviseur
fridge_rentalRefrigerator rentalLocation réfrigérateur
phonebooth_rentalPhone booth rentalLocation phonebooth
appliance_rentalEquipment rentalLocation équipement
furniture_rentalFurniture rentalLocation meuble
food_serviceFood serviceVente de nourriture
garbage_serviceGarbage collection in officesVidage des poubelles dans les bureaux
cleaning_serviceExtra regular cleaningExtra ménage régulier
private_networkPrivate wired networkRéseau privé filaire
private_wifiPrivate WiFi networkRéseau privé WIFI
ev_chargerElectric vehicle chargingRecharge véhicule électrique
access_cardAdditional access cardCarte d'accès supplémentaire
additional_memberAdditional memberMembre supplémentaire
additional_member_tempTemporary additional memberMembre supplémentaire temporaire
service_by_monthOther monthly serviceAutre service mensuel
service_by_month_prepaidPrepaid monthly serviceService mensuel pré-payé
credit_fixedFixed amount creditAvoir en numéraire
credit_percentPercentage creditAvoir en pourcentage
invoice_lineInvoice lineLigne de facture
Credit Options: Options with keys meeting_room_package, credit_fixed, or credit_percent are treated as credit options. They have special fields:
  • priceRemaining: Tracks the remaining credit amount
  • creditUsed: Indicates if the credit has been fully consumed
Credit options are not included in regular monthly billing calculations.

Example Request

curl -X POST \
  -H "Authorization: Bearer ww_dev_your_token_here" \
  -H "Content-Type: application/json" \
  -d '{
    "organizationId": "507f1f77bcf86cd799439011",
    "rentalPrice": "1500€",
    "rentalEndAt": "2025-12-31",
    "rentalRenewal": 12,
    "contractOptionsToAdd": [
      {
        "optionModelId": "507f1f77bcf86cd799439099",
        "key": "parking",
        "name": "Place de parking",
        "quantity": 2,
        "price": 150
      }
    ],
    "roomsToAdd": ["507f191e810c19729de860ea"]
  }' \
  "https://api.workways.com/api/merge-contract"

Success Response Example

{
  "retcode": 1,
  "message": "OK",
  "data": {
    "organizationId": "507f1f77bcf86cd799439011",
    "organizationName": "Acme Corp"
  }
}

Error Response Examples

// Missing required parameter
{
  "retcode": -1,
  "message": "Missing required parameter: organizationId"
}

// Invalid organization ID format
{
  "retcode": -1,
  "message": "Invalid organizationId format"
}

// Organization not found
{
  "retcode": -1,
  "message": "Organization not found"
}

// Unauthorized (no token)
{
  "retcode": -1,
  "message": "Unauthorized: Authorization header is required. Use: Authorization: Bearer <your_token>"
}

Notes

  • Incremental updates: Use contractOptionsToAdd/contractOptionsToRemove and roomsToAdd/roomsToRemove to modify existing lists incrementally.
  • Full replacement: Use contractOptions or rooms to completely replace the existing lists.
  • Dates: All date parameters use YYYY-MM-DD format (e.g., "2025-12-31").

Interactive Demo Application

Try the Workways Public API Demo

Experience the full booking workflow with our interactive demo application. Test all API endpoints in a user-friendly interface.

🚀 Launch Demo App (English) 🚀 Lancer la démo (Français)

Features: Browse sites and rooms, view availability calendars, submit booking requests, register organizations

Booking Workflows

The Workways Public API supports two main booking workflows, with different behavior based on room type and user registration status.

Workflow 1: Meeting Room Booking (publicMeeting)

For rooms with type "publicMeeting"

Step 1: Select a Room and Time Slot

Use the /api/rooms endpoint to find available meeting rooms, then check availability using the /api/icsfeed endpoint to view occupied time slots.

Step 2: Submit Booking Request

POST to /api/meeting-request with room ID, date/time, and contact email.

Step 3: Response Depends on User Status

Scenario A: Existing User (Email Found)
  • Response Status: ACCEPTED
  • Booking State: Confirmed immediately
  • Advance Notice: Can book same-day (but not in the past)
  • Next Step: Booking is confirmed, user receives notification
  • Calendar: Immediately visible in ICS feed
Scenario B: New User (Email Not Found)
  • Response Status: REQUEST_ACCEPTED
  • Booking State: Pending registration
  • Advance Notice: Minimum 2 days in advance
  • Next Step: User must complete registration via /api/register
  • Registration: Includes email validation and password setup
  • Final Confirmation: Booking confirmed after account validation
Scenario C: Archived Account
  • Response Status: ARCHIVED
  • Action Required: Contact administrator to restore account
  • Booking: Not allowed until account is restored

Workflow 2: Office Space Rental (publicOffice)

For rooms with type "publicOffice"

Step 1: Browse Office Spaces

Use the /api/rooms endpoint with type=publicOffice filter to find available office spaces.

Step 2: Direct Registration

Office spaces require a rental contract and cannot be booked via the booking endpoint. Users are directed to the registration form immediately.

Step 3: Submit Organization Registration

POST to /api/register with company details. Include the office space context in the request (optional field or comments).

Step 4: Manual Processing

  • Registration request creates a pending organization
  • User receives email validation link
  • Company number (SIRET/KBO/VAT) is verified if provided
  • Workways team prepares rental contract
  • Once validated, access cards are activated

Note: Office rentals are monthly contracts and require manual approval, unlike meeting rooms which can be instantly booked.

User States and Permissions

User State Same-Day Booking Advance Booking Registration Required
Existing User (Active) ✅ Allowed ✅ No minimum ❌ Not required
New User ❌ Not allowed ✅ Minimum 2 days ✅ Required
Archived User ❌ Not allowed ❌ Not allowed ⚠️ Contact admin

Integration Best Practices

Recommended Implementation Flow:
  1. Room Discovery: Use /api/sites and /api/rooms to list available spaces
  2. Differentiate Room Types: Show different UI for publicMeeting vs publicOffice
    • Meeting rooms → Calendar booking interface
    • Office spaces → Registration/contact form
  3. Check Availability: Use /api/icsfeed to display occupied time slots in your calendar UI
  4. Handle Response Status: Check the status field in responses:
    • ACCEPTED → Booking confirmed, reload calendar
    • REQUEST_ACCEPTED → Redirect to registration form
    • ARCHIVED → Show error message with admin contact
  5. Reload Calendar Feed: After successful booking (ACCEPTED), refresh the ICS feed to show the new occupied slot
  6. Email Validation: Always validate email format before submission
  7. Date Validation: Implement client-side date validation based on user type (if known)

Error Handling

When an error occurs, the API returns a response with retcode: -1 and an appropriate error message.

Error Response Examples

Invalid Country
{
  "retcode": -1,
  "message": "Invalid country: \"XYZ\". Use ISO code (e.g., \"FR\", \"BE\") or country name (e.g., \"France\", \"Belgium\")",
  "data": []
}
Invalid Site ID
{
  "retcode": -1,
  "message": "Invalid site ID format."
}
Internal Server Error
{
  "retcode": -1,
  "message": "Internal server error"
}

Common HTTP Status Codes

HTTP Status Description
200 Success - Request completed successfully
400 Bad Request - Invalid parameters (e.g., invalid country name, invalid site ID format)
401 Unauthorized - Missing, invalid, or revoked API token (POST endpoints only)
500 Internal Server Error - Something went wrong on the server

Use Cases

Integration Examples:
  • Build a public website showing available coworking spaces
  • Create a mobile app for finding meeting rooms
  • Integrate with automation platforms (Zapier, n8n, etc.)
  • Export data to Google Sheets or Airtable
  • Build analytics dashboards for space availability

Rate Limits

Currently, there are no enforced rate limits on these public endpoints. However, please be respectful and avoid excessive requests.

Support

If you have questions or need support, please contact: