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

POST/graphqlπŸ”’ Auth

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

curl -X POST https://id.vyntech.com.au/graphql \ -H "Content-Type: application/json" \ -H "Authorization: Bearer eyJhbGciOiJFZERTQSIs..." \ -d '{ "query": "query Me { me { id email displayName emailVerified mfaEnabled status roles { id name permissions { resource action } } } }" }'
Request Body
{
  "query": "query Me { me { id email displayName emailVerified mfaEnabled status roles { id name permissions { resource action } } } }"
}

List Users with Pagination

POST/graphqlπŸ”’ Auth

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

curl -X POST https://id.vyntech.com.au/graphql \ -H "Content-Type: application/json" \ -H "Authorization: Bearer eyJhbGciOiJFZERTQSIs..." \ -d '{ "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 } }'
Request Body
{
  "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 }
}

Get Tenant Settings

POST/graphqlπŸ”’ Auth

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

curl -X POST https://id.vyntech.com.au/graphql \ -H "Content-Type: application/json" \ -H "Authorization: Bearer eyJhbGciOiJFZERTQSIs..." \ -d '{ "query": "query TenantSettings { tenant { id slug plan status settings { security { mfaEnforced passwordMinLength maxSessions } branding { logoUrl primaryColor } limits { maxUsers maxRoles maxApiKeys } } } }" }'
Request Body
{
  "query": "query TenantSettings { tenant { id slug plan status settings { security { mfaEnforced passwordMinLength maxSessions } branding { logoUrl primaryColor } limits { maxUsers maxRoles maxApiKeys } } } }"
}

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

POST/graphqlπŸ”’ Auth

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

curl -X POST https://id.vyntech.com.au/graphql \ -H "Content-Type: application/json" \ -H "Authorization: Bearer eyJhbGciOiJFZERTQSIs..." \ -d '{ "query": "mutation UpdateProfile($input: UpdateProfileInput!) { updateProfile(input: $input) { id email displayName } }", "variables": { "input": { "displayName": "Jane S." } } }'
Request Body
{
  "query": "mutation UpdateProfile($input: UpdateProfileInput!) { updateProfile(input: $input) { id email displayName } }",
  "variables": { "input": { "displayName": "Jane S." } }
}

Create Role with Permissions

POST/graphqlπŸ”’ Auth

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

curl -X POST https://id.vyntech.com.au/graphql \ -H "Content-Type: application/json" \ -H "Authorization: Bearer eyJhbGciOiJFZERTQSIs..." \ -d '{ "query": "mutation CreateRole($input: CreateRoleInput!) { createRole(input: $input) { id name permissions { resource action } } }", "variables": { "input": { "name": "support-agent", "description": "Can view users and sessions", "permissions": [ { "resource": "users", "action": "read" }, { "resource": "sessions", "action": "read" }, { "resource": "sessions", "action": "revoke" } ] } } }'
Request Body
{
  "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" }
      ]
    }
  }
}

Update Tenant Settings

POST/graphqlπŸ”’ Auth

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

curl -X POST https://id.vyntech.com.au/graphql \ -H "Content-Type: application/json" \ -H "Authorization: Bearer eyJhbGciOiJFZERTQSIs..." \ -d '{ "query": "mutation UpdateSettings($input: TenantSettingsInput!) { updateTenantSettings(input: $input) { id slug settings { security { mfaEnforced passwordMinLength } } } }", "variables": { "input": { "security": { "mfaEnforced": true, "passwordMinLength": 14 } } } }'
Request Body
{
  "query": "mutation UpdateSettings($input: TenantSettingsInput!) { updateTenantSettings(input: $input) { id slug settings { security { mfaEnforced passwordMinLength } } } }",
  "variables": {
    "input": {
      "security": { "mfaEnforced": true, "passwordMinLength": 14 }
    }
  }
}

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.