An app reaches a WoodSystems shop through OAuth. Someone at the shop signs in to WoodSystems, sees what your app is asking to do and says yes, and your app then calls the REST API as that person. The shop never hands you an API key and you never see a password.
Create a developer account
A developer account is free and separate from any shop. Use Create a developer account on this page or in the header. You give your name, your company, your email and a password, and agree to the terms. We email you a link to confirm your address; it works for 24 hours, and you cannot sign in until you have used it.
You then sign in to the WoodSystems web app like anyone else, and see the developer console instead of a shop: your apps, your sandbox and a link to these docs.
A developer account has no live API keys. It reaches shops only through apps they connect.
Your sandbox shop
Confirming your email creates your sandbox: a shop of your own in the sandbox environment at https://api-sandbox.woodsystems.com. It is set up with the standard shop defaults and a small set of demo data to build against:
- 6 contacts
- 5 jobs at different stages
- 2 estimates and 1 invoice
- 1 work order
- 3 tasks and 2 calendar events
The demo records are made the same way the app makes them, so their statuses and history are real. As in every sandbox, emails and texts are recorded but never sent.
The Sandbox page in the developer console shows its status, its base URL and its account id. It also makes wsk_test_ keys for calling the API directly, and resets the sandbox to the demo data when you want a clean start. If the sandbox could not be created when you confirmed your email, the page says Sandbox not created yet and has a button to try again.
Register an app
In the developer console, open Apps and create one. You give:
- A name, a description and a logo URL. Shops see them when they connect.
- Your website, a support email, and links to your privacy policy and terms.
- Redirect URIs, up to 10. Each must be
https, a loopbackhttpaddress (127.0.0.1,localhostor[::1]) or a native app scheme, with no fragment. - Requested scopes: the scopes your app may ask for, chosen from those a connected app can be granted.
Saving the app shows its client id and client secret. The secret starts wss_ and is shown this once, so put it in your server's secret store straight away. Rotate secret makes a new one and shows it once; the old one stops working immediately, so update your server when you rotate.
Every app exists in both environments with the same client id and secret, so the same code runs against the sandbox and live with only the base URL changed. If the copy to the sandbox fails, the console tells you.
Apps registered here are confidential clients: the secret stays on your server, which authenticates to the token endpoint with HTTP Basic (client_secret_basic). They use the authorization_code and refresh_token grants.
Deleting an app ends every connection to it, live and in the sandbox.
Connect a shop with OAuth
Connections use the OAuth 2.1 authorization code flow with PKCE (S256).
| Live | Sandbox | |
|---|---|---|
| Authorize | https://api.woodsystems.com/oauth/authorize | https://api-sandbox.woodsystems.com/oauth/authorize |
| Token | https://api.woodsystems.com/oauth/token | https://api-sandbox.woodsystems.com/oauth/token |
| Revoke | https://api.woodsystems.com/oauth/revoke | https://api-sandbox.woodsystems.com/oauth/revoke |
| Server metadata | https://api.woodsystems.com/.well-known/oauth-authorization-server | https://api-sandbox.woodsystems.com/.well-known/oauth-authorization-server |
Build and test against the sandbox first. The Test in sandbox panel on your app's page shows the authorize URL for the sandbox and for live.
1. Send the person to WoodSystems
Make a code verifier: 43 to 128 random characters from A-Z, a-z, 0-9, -, ., _ and ~. The code challenge is its SHA-256 hash, base64url encoded without padding. Keep the verifier with the person's session and send their browser to the authorize URL:
https://api.woodsystems.com/oauth/authorize?response_type=code&client_id=YOUR_CLIENT_ID&redirect_uri=https%3A%2F%2Fyourapp.example%2Fcallback&scope=jobs%3Aread%20contacts%3Aread&state=RANDOM_STATE&code_challenge=CODE_CHALLENGE&code_challenge_method=S256
redirect_urimust match one of your registered redirect URIs exactly. You may leave it out only when the app has exactly one.scopelists the scopes you need, separated by spaces. Asking for a scope outside your app's requested scopes is refused withinvalid_scope.stateis returned to you unchanged; check it. It can be up to 1,024 characters.
The person signs in to WoodSystems, sees who they are connecting as, their role, your app and what it asks to do, and chooses what to allow. Finding the account's statuses, fields and team (meta:read) is always included. They have 10 minutes to answer.
2. Take the code
WoodSystems sends the browser back to your redirect URI with code, state and iss. If the person declines you get error=access_denied; other problems come back as error and error_description. The code works once and only for 60 seconds.
3. Exchange it for tokens
Post the code and the verifier to the token endpoint, with your client id and secret as HTTP Basic credentials:
curl https://api.woodsystems.com/oauth/token \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-d grant_type=authorization_code \
-d code="$CODE" \
-d redirect_uri=https://yourapp.example/callback \
-d code_verifier="$CODE_VERIFIER"
{
"access_token": "wsa_...",
"token_type": "Bearer",
"expires_in": 3600,
"scope": "jobs:read contacts:read meta:read",
"refresh_token": "wsr_..."
}
A code presented twice ends whatever the first exchange issued.
4. Call the API
Send the access token as a bearer token. No X-Tenant-Id is needed: the token belongs to one account.
curl https://api.woodsystems.com/v1/account \
-H "Authorization: Bearer $ACCESS_TOKEN"
5. Refresh
Access tokens last an hour. Refresh tokens last 30 days and change on every use: store the new one each time. A refresh token presented twice ends the connection, and the person has to connect again. Add scope to keep fewer permissions than were granted.
curl https://api.woodsystems.com/oauth/token \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-d grant_type=refresh_token \
-d refresh_token="$REFRESH_TOKEN"
To disconnect, post the token to the revoke endpoint with the same credentials. That ends the whole connection.
Review
A new app is unreviewed. Unreviewed apps work, with two differences:
- They can be connected to at most 10 live shops. The next shop is refused with "This app has not been reviewed by WoodSystems yet and has reached its limit of 10 connected accounts." Sandbox connections are never limited.
- The consent screen says Not reviewed by WoodSystems.
When your app is ready, use Submit for review on its page; you can withdraw it while it is in review. WoodSystems approves or rejects it, and a rejection comes with a note saying why. The owners of your developer account get an email with the outcome.
An approved app shows a Verified badge on the consent screen, has no limit on connected shops unless WoodSystems sets one for it, and can be listed in the app directory. Editing an approved app keeps it approved, and WoodSystems can see what changed since the approval. WoodSystems can also block an app, which stops its connections working.
The app directory
Approved apps that are listed appear in the app directory on this site and to shops in WoodSystems under Settings, Integrations, App directory, with their permissions in plain words. A shop's Connect button opens your website, and your app starts the OAuth flow from there, so give your website a clear way to start connecting.
Who a connection acts as
An access token acts as the person who connected your app, with their role in WoodSystems as it is today. If an admin changes their role, the next call follows the new role.
- Your app can never do more than that person could do in the WoodSystems web app, and only what the granted scopes allow.
- An admin decides who may connect apps at all, under Settings, Integrations, Connected apps. Admins see every connection there and can disconnect any of them.
What an app can never do
These are out of reach for any connected app, whatever it asks for and whatever the person's role:
- Account settings, statuses, custom fields, workflows or templates
- Users, roles, API keys, webhooks or connected apps
- Deleting anything
- Payments, refunds or billing
- Sending documents for signature
These stay in the WoodSystems web app. The REST API has its own list of what is not available.