Docs/Account/Integration/GraphQL

GraphQL Integration

Vyntech Account exposes a GraphQL API at https://id.vyntech.com.au/graphql. Use it when you need flexible queries, want to fetch related data in a single request, or prefer GraphQL tooling.

Endpoint & Authentication

Endpoint:https://id.vyntech.com.au/graphql
Method:POST
Content-Type:application/json
Auth:Bearer token in Authorization header

Introspection is enabled for authenticated requests. Use your access token from the POST /api/v1/auth/login response.

Schema Overview

The schema exposes the following main types, matching the core entities described in the Concepts page:

User
Tenant
Session
Role
Permission
Policy
ApiKey
type Query {
  me: User!
  user(id: ID!): User
  users(first: Int, after: String, filter: UserFilter): UserConnection!
  tenant: Tenant!
  sessions(first: Int, after: String): SessionConnection!
  roles: [Role!]!
  role(id: ID!): Role
  policies: [Policy!]!
  apiKeys: [ApiKey!]!
}

type Mutation {
  updateProfile(input: UpdateProfileInput!): User!
  updatePassword(input: UpdatePasswordInput!): Boolean!
  enableMfa(input: EnableMfaInput!): MfaSetupPayload!
  disableMfa(code: String!): Boolean!
  revokeSession(id: ID!): Boolean!
  revokeAllSessions: Int!
  createRole(input: CreateRoleInput!): Role!
  updateRole(id: ID!, input: UpdateRoleInput!): Role!
  deleteRole(id: ID!): Boolean!
  assignRole(userId: ID!, roleId: ID!): User!
  removeRole(userId: ID!, roleId: ID!): User!
  createPolicy(input: CreatePolicyInput!): Policy!
  updateTenantSettings(input: TenantSettingsInput!): Tenant!
  createApiKey(input: CreateApiKeyInput!): ApiKeyPayload!
  revokeApiKey(id: ID!): Boolean!
}

Example Queries

All requests are POST /graphql with a JSON body containing query and optional variables.

Get Current User with Roles


graphqlAuth

Fetch the authenticated user's profile along with their assigned roles and permissions.

Payload Example

{
  "query": "query Me { me { id email displayName emailVerified mfaEnabled status roles { id name permissions { resource action } } } }"
}

Status Codes

  • Name
    200
    Type
    HTTP
    Description
    Query executed successfully
  • Name
    401
    Type
    HTTP
    Description
    Missing or invalid bearer token
Request
POST/graphql
Response 200

List Users with Pagination


graphqlAuth

Retrieve a paginated list of users in the current tenant with cursor-based pagination.

Payload Example

{
  "query": "query Users($first: Int, $after: String) { users(first: $first, after: $after) { edges { node { id email displayName status } cursor } pageInfo { hasNextPage endCursor } } }",
  "variables": { "first": 10, "after": null }
}

Status Codes

  • Name
    200
    Type
    HTTP
    Description
    Query executed successfully
  • Name
    401
    Type
    HTTP
    Description
    Missing or invalid bearer token
  • Name
    403
    Type
    HTTP
    Description
    Insufficient permissions — requires users:read
Request
POST/graphql
Response 200

Get Tenant Settings


graphqlAuth

Fetch the current tenant's configuration including security, branding, and limits.

Payload Example

{
  "query": "query TenantSettings { tenant { id slug plan status settings { security { mfaEnforced passwordMinLength maxSessions } branding { logoUrl primaryColor } limits { maxUsers maxRoles maxApiKeys } } } }"
}

Status Codes

  • Name
    200
    Type
    HTTP
    Description
    Query executed successfully
  • Name
    401
    Type
    HTTP
    Description
    Missing or invalid bearer token
  • Name
    403
    Type
    HTTP
    Description
    Insufficient permissions — requires settings:read
Request
POST/graphql
Response 200

Example Mutations

Mutations follow the same request format. They return the modified object so you can update your local state without a subsequent query.

Update User Profile


graphqlAuth

Update the authenticated user's display name or other profile fields.

Payload Example

{
  "query": "mutation UpdateProfile($input: UpdateProfileInput!) { updateProfile(input: $input) { id email displayName } }",
  "variables": { "input": { "displayName": "Jane S." } }
}

Status Codes

  • Name
    200
    Type
    HTTP
    Description
    Mutation executed successfully
  • Name
    401
    Type
    HTTP
    Description
    Missing or invalid bearer token
Request
POST/graphql
Response 200

Create Role with Permissions


graphqlAuth

Create a new custom role and assign permissions in a single request.

Payload Example

{
  "query": "mutation CreateRole($input: CreateRoleInput!) { createRole(input: $input) { id name permissions { resource action } } }",
  "variables": {
    "input": {
      "name": "support-agent",
      "description": "Can view users and sessions but cannot modify roles",
      "permissions": [
        { "resource": "users", "action": "read" },
        { "resource": "sessions", "action": "read" },
        { "resource": "sessions", "action": "revoke" }
      ]
    }
  }
}

Status Codes

  • Name
    200
    Type
    HTTP
    Description
    Role created successfully
  • Name
    401
    Type
    HTTP
    Description
    Missing or invalid bearer token
  • Name
    403
    Type
    HTTP
    Description
    Insufficient permissions — requires roles:write
  • Name
    422
    Type
    HTTP
    Description
    Role name already exists in this tenant
Request
POST/graphql
Response 200

Update Tenant Settings


graphqlAuth

Merge-update the tenant's security and branding settings.

Payload Example

{
  "query": "mutation UpdateSettings($input: TenantSettingsInput!) { updateTenantSettings(input: $input) { id slug settings { security { mfaEnforced passwordMinLength } } } }",
  "variables": {
    "input": {
      "security": { "mfaEnforced": true, "passwordMinLength": 14 }
    }
  }
}

Status Codes

  • Name
    200
    Type
    HTTP
    Description
    Settings updated successfully (merge semantics)
  • Name
    401
    Type
    HTTP
    Description
    Missing or invalid bearer token
  • Name
    403
    Type
    HTTP
    Description
    Insufficient permissions — requires settings:write
  • Name
    422
    Type
    HTTP
    Description
    Invalid setting value
Request
POST/graphql
Response 200

Pagination

All list queries use cursor-based pagination following the Relay specification. This ensures stable pagination even when items are added or removed between requests.

Forward Pagination

Use first (page size) and after (cursor from previous page) to paginate forward.

Backward Pagination

Use last (page size) and before (cursor) to paginate backward from the end.

Every connection returns edges (array of { node, cursor }) and a pageInfo object:

type PageInfo {
  hasNextPage: Boolean!
  hasPreviousPage: Boolean!
  startCursor: String
  endCursor: String
}

type UserEdge {
  node: User!
  cursor: String!
}

type UserConnection {
  edges: [UserEdge!]!
  pageInfo: PageInfo!
  totalCount: Int!
}

Error Handling

GraphQL always returns HTTP 200 for successfully parsed requests. Errors are returned in the errors array alongside any partial data. Each error includes machine-readable extension codes.

{
  "data": null,
  "errors": [
    {
      "message": "You do not have permission to access this resource",
      "path": ["users"],
      "extensions": {
        "code": "FORBIDDEN",
        "statusCode": 403
      }
    }
  ]
}

Extension codes map to their REST equivalents:

Extension CodeHTTP EquivalentDescription
UNAUTHENTICATED401Missing or expired bearer token
FORBIDDEN403Valid token but insufficient permissions
NOT_FOUND404Requested resource does not exist
VALIDATION_ERROR422Input failed validation (details in message)
RATE_LIMITED429Too many requests — back off and retry

Important: Always check both data and errors in your response handling. GraphQL can return partial data alongside errors for nullable fields.

Subscriptions

WebSocket subscriptions are available for real-time events (the same events available via webhooks). Connect via wss://id.vyntech.com.au/graphql using the graphql-ws protocol and pass your bearer token in connectionParams.

// Available subscriptions
type Subscription {
  userCreated: User!
  userUpdated: User!
  userDeleted: ID!
  sessionCreated: Session!
  sessionRevoked: ID!
  roleChanged: RoleChangeEvent!
  settingsUpdated: Tenant!
}

// Connection example (graphql-ws protocol)
{
  "type": "connection_init",
  "payload": {
    "authorization": "Bearer eyJhbGciOiJFZERTQSIs..."
  }
}
import { createClient } from "graphql-ws";

const client = createClient({
  url: "wss://id.vyntech.com.au/graphql",
  connectionParams: {
    authorization: "Bearer eyJhbGciOiJFZERTQSIs...",
  },
});

// Subscribe to new user events
const unsubscribe = client.subscribe(
  {
    query: `subscription {
      userCreated { id email displayName status }
    }`,
  },
  {
    next: (data) => console.log("New user:", data),
    error: (err) => console.error("Subscription error:", err),
    complete: () => console.log("Subscription closed"),
  }
);

Client Libraries

You can use any GraphQL client with the Vyntech Account endpoint. Here are recommended libraries by language:

JavaScript / TypeScript

Apollo Client, urql, graphql-request

Python

gql (with requests transport)

Go

github.com/hasura/go-graphql-client

PHP

php-graphql-client (softonic/graphql-client)

Example setup with Apollo Client:

import { ApolloClient, InMemoryCache, createHttpLink } from "@apollo/client";
import { setContext } from "@apollo/client/link/context";

const httpLink = createHttpLink({
  uri: "https://id.vyntech.com.au/graphql",
});

const authLink = setContext((_, { headers }) => ({
  headers: {
    ...headers,
    authorization: `Bearer ${getAccessToken()}`,
  },
}));

const client = new ApolloClient({
  link: authLink.concat(httpLink),
  cache: new InMemoryCache(),
});

// Usage with React
import { useQuery, gql } from "@apollo/client";

const ME_QUERY = gql`
  query Me {
    me { id email displayName roles { name } }
  }
`;

function Profile() {
  const { data, loading, error } = useQuery(ME_QUERY);
  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;
  return <h1>Hello, {data.me.displayName}</h1>;
}

Best Practices

Use Persisted Queries

In production, register your queries with an SHA-256 hash and send only the hash. This reduces payload size and improves security by preventing arbitrary queries.

Request Only What You Need

Select only the fields your UI requires. Avoid over-fetching — it increases response time and consumes more server resources for field resolution.

Use Fragments for Shared Types

Define reusable fragments for commonly selected type fields (e.g., a UserCore fragment). This keeps queries DRY and easier to maintain.

Handle Errors Properly

Always check both data and errors in responses. Use the extensions.code field to programmatically handle different error types.

What's Next

We use cookies and similar technologies to measure traffic and improve the site. You can choose which categories to allow. Manage Preferences.