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
https://id.vyntech.com.au/graphqlapplication/jsonIntrospection 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:
UserTenantSessionRolePermissionPolicyApiKeytype 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
/graphql🔒 AuthFetch the authenticated user's profile along with their assigned roles and permissions.
{
"query": "query Me { me { id email displayName emailVerified mfaEnabled status roles { id name permissions { resource action } } } }"
}List Users with Pagination
/graphql🔒 AuthRetrieve a paginated list of users in the current tenant with cursor-based pagination.
{
"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
/graphql🔒 AuthFetch the current tenant's configuration including security, branding, and limits.
{
"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
/graphql🔒 AuthUpdate the authenticated user's display name or other profile fields.
{
"query": "mutation UpdateProfile($input: UpdateProfileInput!) { updateProfile(input: $input) { id email displayName } }",
"variables": { "input": { "displayName": "Jane S." } }
}Create Role with Permissions
/graphql🔒 AuthCreate a new custom role and assign permissions in a single request.
{
"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
/graphql🔒 AuthMerge-update the tenant's security and branding settings.
{
"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 Code | HTTP Equivalent | Description |
|---|---|---|
| UNAUTHENTICATED | 401 | Missing or expired bearer token |
| FORBIDDEN | 403 | Valid token but insufficient permissions |
| NOT_FOUND | 404 | Requested resource does not exist |
| VALIDATION_ERROR | 422 | Input failed validation (details in message) |
| RATE_LIMITED | 429 | Too 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
gRPC Integration →
High-performance binary protocol for backend-to-backend communication.
OAuth 2.1 / OIDC →
Standard authorization flows and OpenID Connect identity layer.
REST API Reference →
Full Authentication API reference with all endpoints and status codes.
Webhooks Guide →
Receive real-time event notifications via HTTP callbacks.