Authentication and authorization
Register companies and contacts (B2B), register customers (B2C), log in, manage access tokens and implement session handling using Propeller's GraphQL API.
API key setup
Every request to the GraphQL API requires an apiKey header. Propeller provides two key types:
- System API key has full access and is for server-side use only. Never expose this in frontend code.
- Limited API key provides anonymous access for frontend use. Supports browsing products, creating carts and registering users.
curl https://api.propellercommerce.com/graphql \
-X POST \
-H "Content-Type: application/json" \
-H "apiKey: YOUR_LIMITED_API_KEY" \
-d '{"query": "{ viewer { __typename } }"}'
Once a user logs in, add the access token alongside the API key:
curl https://api.propellercommerce.com/graphql \
-X POST \
-H "Content-Type: application/json" \
-H "apiKey: YOUR_LIMITED_API_KEY" \
-H "Authorization: Bearer ACCESS_TOKEN" \
-d '{"query": "{ viewer { firstName lastName email } }"}'
Registering a company and contact (B2B)
B2B registration is a two-step process: first create the company, then register a contact within it.
Step 1: Create the company
mutation {
companyCreate(
input: {
name: "Acme Corp"
taxNumber: "NL123456789B01"
}
) {
companyId
name
}
}
Expected response:
{
"data": {
"companyCreate": {
"companyId": 456,
"name": "Acme Corp"
}
}
}
Step 2: Register a contact
Use the companyId from step 1 as the parentId:
mutation {
contactRegister(
input: {
firstName: "Jane"
lastName: "Smith"
email: "jane@acme.com"
phone: "020-7654321"
password: "secure_password"
parentId: 456
}
) {
contact {
userId
firstName
lastName
email
}
session {
accessToken
refreshToken
expirationTime
}
}
}
Expected response:
{
"data": {
"contactRegister": {
"contact": {
"userId": 789,
"firstName": "Jane",
"lastName": "Smith",
"email": "jane@acme.com"
},
"session": {
"accessToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "AOEOulYnMD7FqivLPCPy2w3bXn...",
"expirationTime": "2026-02-25T15:00:00.000Z"
}
}
}
}
Registering a customer (B2C)
Use customerRegister to create a B2C customer account. The mutation returns a session with access and refresh tokens, so the user is immediately logged in.
mutation {
customerRegister(
input: {
firstName: "John"
lastName: "Doe"
email: "john@example.com"
phone: "020-1234567"
password: "secure_password"
}
) {
session {
accessToken
refreshToken
expirationTime
}
customer {
__typename
firstName
lastName
email
userId: customerId
}
}
}
Expected response:
{
"data": {
"customerRegister": {
"session": {
"accessToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "AOEOulYnMD7FqivLPCPy2w3bXn...",
"expirationTime": "2026-02-25T15:00:00.000Z"
},
"customer": {
"__typename": "Customer",
"firstName": "John",
"lastName": "Doe",
"email": "john@example.com",
"userId": 12345
}
}
}
}
Logging in
Use the login mutation to authenticate an existing user (works for both customers and contacts):
mutation {
login(
input: {
email: "john@example.com"
password: "secure_password"
}
) {
session {
accessToken
refreshToken
expirationTime
}
}
}
Expected response:
{
"data": {
"login": {
"session": {
"accessToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"refreshToken": "AOEOulYnMD7FqivLPCPy2w3bXn...",
"expirationTime": "2026-02-25T15:00:00.000Z"
}
}
}
}
The response contains:
| Field | Description |
|---|---|
accessToken | Bearer token for authenticated API requests. Expires after 1 hour. |
refreshToken | Token to obtain new access tokens. Does not expire unless the user is deleted, disabled or changes their password/email. |
expirationTime | Timestamp when the access token expires. |
The viewer query
After logging in, use viewer to retrieve the current user's details:
query {
viewer {
__typename
firstName
middleName
lastName
email
phone
mobile
isLoggedIn
... on Contact {
userId: contactId
company {
companyId
name
addresses {
id
street
number
city
postalCode
country
type
isDefault
}
}
}
... on Customer {
userId: customerId
addresses {
id
street
number
city
postalCode
country
type
isDefault
}
}
}
}
Expected response (for a contact):
{
"data": {
"viewer": {
"__typename": "Contact",
"firstName": "Jane",
"middleName": null,
"lastName": "Smith",
"email": "jane@acme.com",
"phone": "020-7654321",
"mobile": null,
"isLoggedIn": true,
"userId": 789,
"company": {
"companyId": 456,
"name": "Acme Corp",
"addresses": [
{
"id": 101,
"street": "Keizersgracht",
"number": "100",
"city": "Amsterdam",
"postalCode": "1015 AA",
"country": "NL",
"type": "delivery",
"isDefault": "Y"
}
]
}
}
}
}
The response type depends on who is logged in. A Contact is a B2B user where addresses come from the company. A Customer is a B2C user where addresses are on the customer directly.
Use __typename to determine which type and render the appropriate UI.
Token lifecycle
Refreshing tokens
When the access token expires, use exchangeRefreshToken to get a new one:
mutation {
exchangeRefreshToken(
input: {
refreshToken: "your_refresh_token"
}
) {
access_token
refresh_token
expires_in
token_type
user_id
}
}
Expected response:
{
"data": {
"exchangeRefreshToken": {
"access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"refresh_token": "AOEOulYnMD7FqivLPCPy2w3bXn...",
"expires_in": 3600,
"token_type": "Bearer",
"user_id": "12345"
}
}
}
This returns a new access token and refresh token. Store them and continue making authenticated requests.
Token expiry details
| Token | Lifetime |
|---|---|
| Access token | 3600 seconds (1 hour) |
| Refresh token | Does not expire |
Refresh tokens are invalidated when:
- The user is deleted
- The user is disabled
- The user changes their password or email
Password reset
Generate a password reset link with passwordResetLink:
mutation {
passwordResetLink(
input: {
email: "john@example.com"
redirectUrl: "https://yourstore.com/login"
language: "en"
}
)
}
On success, this returns a reset link that you can include in a password reset email. The redirectUrl is where the user will be redirected after resetting their password.
Logging out
There is no explicit logout mutation. To log a user out:
- Remove the stored access token and refresh token from your client-side storage
- Optionally start a new anonymous session
Next steps
- Understanding companies, contacts and customers for how companies, contacts and customers relate
- Managing addresses to manage customer and company addresses
- Checkout flow to use viewer data to pre-fill checkout addresses