Developer API

1 Introduction

The Geora protocol is a blockchain-based system for managing and securing sustainable supply chains. It provides simple tools and workflows to create solutions that provide rich data traceability, secure payments and real time finance.

This page provides reference documentation for the Geora GraphQL API. For learning-focused content and tutorials, please see https://help.geora.io.

The API schema is directly generated from this document, and you can find the generated output file here.

1.1 API status and stability

Different parts of the Geora API are classified according to their stability. Each of these stability levels have certain guarantees for the user.

LIVE

Stable. No breaking changes to the API without consultation and approval from existing users

BETA

Stable. Breaking changes will only occur for critical security issues.

Geora uses beta APIs in our own product and production projects, but they are newer APIs that may contain some small bugs.

ALPHA

Unstable. Breaking changes may occur at any time. Do not use in production.

Geora provides early documentation of alpha APIs to encourage feedback from potential users - please feel free to contact us with thoughts and ideas on these APIs.

We include the following as breaking changes:

We do NOT class the following as breaking changes:

Please note that we are in the process of making many fields in the API non-nullable which were previously nullable. For most users this will have no effect, but if you generate strong types from the GraphQL schema definition you should be aware of this change! Please reach out if this is a concern for you so we can keep you up to date.

The @alpha and @beta directives appear on all fields with a non-live status. You can use this during schema introspection to filter out APIs based on their stability.

<<>>schema
directive @alpha on FIELD_DEFINITION directive @beta on FIELD_DEFINITION

1.2 Authentication and authorisation

Geora uses expiring JWTs for authentication. Read more here.

The queries and mutations which require a token are marked with the requiresActor or requiresActorOrCapability directives.

<<>>schema
directive @requiresActor on FIELD_DEFINITION directive @requiresActorOrCapability on FIELD_DEFINITION

1.3 Premium features

While most of the Geora API is free to use, some features like theming and unlimited certificates require a paid account (see here). The requiresPayingUser directive enforces that the calling user has the required minimum subscription plan.

<<>>schema
directive @requiresPayingUser(plan: SubscriptionPlan!) on FIELD_DEFINITION
<<>>domain-helpers | schema
enum SubscriptionPlan { FREE STARTER PRO BUSINESS }

2 Basics

This section explains some of the common ideas that are shared across the Geora API.

2.1 Timestamps, dates, and times

The TimestampUTC scalar returns an ISO-8601 timestamp string in the UTC zone. TimestampTZ does the same but includes a time zone specifier.

<<>>domain-helpers | schema
scalar TimestampUTC scalar TimestampTZ

The Date type represents a date as an ISO-8601 extended format calendar date string, like 2006-08-14.

<<>>schema
scalar Date

The TimeTZ type represents a time of day as a ISO-8601 time carrying a time zone, like T02:34:56-06:00.

<<>>schema
scalar TimeTZ

2.2 Node

You can think of the Geora data model as a graph, containing objects and their links to each other. For example, an asset is linked to actors by ownership and permissions, and files are linked to assets by evidence attachments.

The Node interface is implemented by all objects (or vertices) in this graph:

The type for each of these vertices extends Node.

<<>>schema
interface Node { id: ID! }

The node ID is a globally unique identifier for the vertex. Using unique IDs permits a standardised refetch query called node, which can be used to efficiently implement UI caching and refetching.

2.3 Connection

A connection is used where a query or field returns a list of objects; it provides cursor-based pagination in an efficient way. A connection contains a list of edges, each linking to another node. For example, referring to a

<<>>schema
interface Connection { # edges: [Edge!]! -- commented because GraphQL doesn't let us write this pageInfo: PageInfo! }

Any type ending in Connection must provide these fields:

edges

a list of edges.

pageInfo

an object which provides pagination information.

Queries that return a Connection support forward pagination via the first and after arguments:

first

requests a certain number of edges to be returned (default: 10)

after

specifies an edge cursor from which to start (default: start of list)

The PageInfo object provides metadata that helps the client determine whether there are more pages to iterate through. It has fields startCursor and endCursor which provide the cursor values of the first and last edges in the list.

<<>>schema
type PageInfo { hasNextPage: Boolean! hasPreviousPage: Boolean! totalPages: Int startCursor: Cursor endCursor: Cursor }

Cursors are opaque base64-encoded strings which uniquely identify an edge in the connection.

<<>>schema
scalar Cursor

2.3.1 Edge

An edge represents one item in the paginated list and must provide these fields:

node

the item (e.g. an Asset)

cursor

a string identifying this edge for purposes of pagination

<<>>schema
interface Edge { # node: Node! -- commented because graphql doesn't let us write this. cursor: Cursor! }

2.3.2 Using connections

It helps to see a concrete example of using a connection. In this case, the caller wants to see all assets that they own. They use the actor > assets query, which returns a connection.

To see the first page of assets, they request 10 items in the connection:

{
  actor(id: "YWN0b3J8NzJjN2U5YzMtOGViNC00ZjIyLWE5NDktYjljMTVkYTUzNzYw") {
    assets(first: 10) {
      pageInfo {
        hasNextPage
        hasPreviousPage
        startCursor
        endCursor
      }
      edges { cursor node { id } }
    }
  }
}

This returns the ten assets under the edges key, as well as a PageInfo object that looks like:

{
    "hasNextPage":true,
    "hasPreviousPage":false,
    "startCursor":"YXNzZXRWZXJzaW9ufDg3MjM0ODk3Mjg5NDcyMwo=",
    "endCursor":"YXNzZXRWZXJzaW9ufDg3MzIxMjQyMzQzMjQK"
}

From the response, the caller can see that there is at least one more page of assets available. For the next query, they set the after argument to the value of endCursor, in order to see the next ten assets.

{
  actor(id: "YWN0b3J8NzJjN2U5YzMtOGViNC00ZjIyLWE5NDktYjljMTVkYTUzNzYw") {
    assets(first: 10, after: "YXNzZXRWZXJzaW9ufDg3MzIxMjQyMzQzMjQK") {
      pageInfo {
        hasNextPage
        hasPreviousPage
        startCursor
        endCursor
      }

      edges {
        cursor
        node {
          id
        }
      }
    }
  }
}

When this query returns, hasNextPage is false and there are no more assets to view.

2.4 Standards

The Geora API implements the following GraphQL standards:

2.5 Directives

These directives are used for API implementation and should not have an effect on usage.

<<>>schema
directive @f( name: String!, afterDefault: Boolean = False, nullOnNotFound: Boolean = False, transform: Boolean = False ) on FIELD_DEFINITION directive @id(t: String) on FIELD_DEFINITION directive @jm( connection: Boolean = False, orderable: [Orderable!] = [], id: String = "id", idType: String = "INT8", query: Boolean = False ) on FIELD_DEFINITION directive @jmf( connection: Boolean = False, orderable: [Orderable!] = [], transform: Boolean = False ) on FIELD_DEFINITION

3 Domain model

This section describes the various objects exposed by the Geora API; in particular the top-level objects that inherit from the Node interface.

3.1 Assets

An Asset is a record containing the history of a supply-chain product.

<<>>schema
<<domain-helpers>> type Asset implements Node { id: ID! @id(t: "asset") <<domain-asset-methods>> }
<<>>domain-asset-methods | schema
assetStandards: [AssetStandard!]! stage: AssetStandardStage! quantity(convertTo: Unit): [Quantity!]! @jmf status: AssetStatus!
<<>>domain-asset-methods | schema
history(assetStandard: ID): AssetHistory!
<<>>domain-asset-methods | schema
chain: Chain!

A Unit is the symbol for Geora’s supported measurement units.

<<>>domain-helpers | schema
scalar Unit

A Unit can be provided as an argument to the quantity field to convert the primaryValue of the asset into that unit.

<<>>domain-helpers | schema
type Quantity { primaryValue: Float! primaryUnit: String! stage: AssetStandardStage! primaryUnitName: String primaryUnitPlural: String primaryUnitSymbol: Unit }

The primaryUnitName, primaryUnitPlural and primaryUnitSymbol will be null if the supplied primaryUnit on the asset does not match a Geora-supported measurement unit.

The supportedUnits query can be used to return all of the units that Geora supports.

<<>>query-queries | schema
supportedUnits( first: Int last: Int after: Cursor before: Cursor ): SupportedUnitsConnection! @jm(connection: true, query: true)
<<>>domain-helpers | schema
type SupportedUnitsConnection implements Connection { pageInfo: PageInfo! edges: [SupportedUnitsEdge!]! } type SupportedUnit { name: String! symbol: Unit! type: String! plural: String! baseUnit: Unit! conversionFactor: Float! } type SupportedUnitsEdge implements Edge { cursor: Cursor! node: SupportedUnit! }

The AssetStatus indicates whether an asset has been burned (made read-only), or moved to another blockchain. In the future, more asset statuses may be added as options in this enum, such as an IN_ESCROW status for finanical agreements.

<<>>domain-helpers | schema
enum AssetStatus { ACTIVE BURNED BRIDGING BRIDGED}

Any actor that has partial or full ownership of an asset will be present in the actorRelationships connection.

<<>>domain-asset-methods | schema
actorRelationships( first: Int last: Int after: Cursor before: Cursor ): ActorRelationshipConnection @jmf(connection: true)
<<>>domain-helpers | schema
type ActorRelationshipConnection implements Connection { pageInfo: PageInfo! edges: [ActorRelationshipEdge!]! }

The connection contains the related actor as its node, but additionally adds relationship data to the edges, codified in the ActorRelationshipEdge type.

ownershipPercentage is a percentage value between 0 and 1, but is DEPRECATED. Due to the behaviour of the GraphQL Float type, in some scenarios you will experience rounding errors which cause ownership transfers to fail. Please use exactOwnershipPercentage instead.

<<>>domain-helpers | schema
scalar Decimal

exactOwnershipPercentage is a percentage value between 0 and 1. This has a different internal representation to ownershipPercentage.

<<>>domain-helpers | schema
type ActorRelationshipEdge implements Edge { cursor: Cursor! node: Actor! isOwner: Boolean! exactOwnershipPercentage: Decimal! }

Assets are related to other assets by relationships like collections or parenthood.

A collection is an asset which contains one or more collected assets. This represents scenarios like a grain silo; where the silo is an asset containing multiple barley assets.

A parent is an abstract “precursor” asset to the current asset. This can be used to represent processing, as in the processing of hops to make beer, or the merging of multiple assets into one.

<<>>domain-asset-methods | schema
assetRelationships( first: Int last: Int after: Cursor before: Cursor ): AssetRelationshipConnection @jmf(connection: true)
<<>>domain-helpers | schema
type AssetRelationshipConnection implements Connection { pageInfo: PageInfo! edges: [AssetRelationshipEdge!]! } type AssetRelationshipEdge implements Edge { cursor: Cursor! node: Asset! relationship: AssetRelationshipType! stage: AssetStandardStage! }

Each asset relationship edge contains the relationship type. When viewing the asset, the bidirectional nature of relationships is shown; when asset A COLLECTS asset B, asset B is COLLECTED_BY asset A, and when asset C is a PARENT_OF asset D, asset D is a CHILD_OF asset C.

When creating relationships, AssetRelationshipInputType is used instead.

<<>>domain-helpers | schema
enum AssetRelationshipType { COLLECTS COLLECTED_BY PARENT_OF CHILD_OF } enum AssetRelationshipInputType { COLLECTS COLLECTED_BY CHILD_OF PARENT_OF }

Assets may have claims made against them, which are key-value pairs asserting information about the asset. Substantiated claims are backed up by file attachments called evidence.

<<>>domain-asset-methods | schema
claims( first: Int last: Int after: Cursor before: Cursor ): ClaimConnection! @jmf(connection: true) attachments: [Attachment!]!
<<>>domain-helpers | schema
type ClaimConnection implements Connection { pageInfo: PageInfo! edges: [ClaimEdge!]! } type ClaimEdge implements Edge { cursor: Cursor! node: Claim! }

Updates provide the link to past versions of an asset. On UpdateEdge, the location will always be PAST, representing the update occurring backwards in time in relation to the queried version. Only successful updates are shown in this connection, and are listed in reverse chronological order.

<<>>domain-asset-methods | schema
updates( first: Int last: Int after: Cursor before: Cursor ): AssetUpdatesConnection @jmf(connection: true)
<<>>domain-helpers | schema
type AssetUpdatesConnection implements Connection { pageInfo: PageInfo! edges: [AssetUpdateEdge!]! } type AssetUpdateEdge implements Edge { cursor: Cursor! node: Update! }

Updates in the PAST were made before (further back in time) than the currently-queried version, and assets in the FUTURE were made after. The update which created the currently-queried version will be the newest asset in the PAST.

The certifications of an asset are found in the certifications connection.

<<>>domain-asset-methods | schema
certifications( first: Int last: Int after: Cursor before: Cursor ): CertificationConnection @jmf(connection: true)
<<>>domain-helpers | schema
type CertificationConnection implements Connection { pageInfo: PageInfo! edges: [AssetCertificationEdge!]! } type AssetCertificationEdge implements Edge { cursor: Cursor! node: Registry! certifications: [CertificationStatus!]! }

3.2 Claims and evidence

A claim is a key-value pair asserting some property of an asset, which may be backed by evidence files. Claim labels are case-insensitive, and the API gives no guarantees on the case of the returned label. Claims differ from the other types by not implementing the Node interface.

<<>>schema
type Claim { id: ID! label: String! rawValue: String! value: TypedClaimValue! stage: AssetStandardStage evidence( first: Int last: Int after: Cursor before: Cursor ): EvidenceConnection @jmf(connection: true) } type Attachment { name: String! url: URL! mimetype: String! uploadedAt: TimestampUTC! }
<<>>domain-helpers | schema
type EvidenceConnection implements Connection { pageInfo: PageInfo! edges: [EvidenceEdge!]! } type EvidenceEdge implements Edge { cursor: Cursor! node: File! }

The value of a claim is associated with a type. This provides more information about the claim than is stored in the rawValue.

Below is a list of supported types:

Claim Type Description Example
TextClaimValue NumberClaimValue QuantityClaimValue An arbitrary string that can have any value. A floating point, double-precision number. A floating point magnitude and a unit of measurement. "APW1" 112.2 90.2mt
<<>>schema
enum ClaimType { TEXT NUMBER QUANTITY DATETIME } union TypedClaimValue = TextClaimValue | NumberClaimValue | QuantityClaimValue | DateTimeClaimValue type TextClaimValue { value: String! } type QuantityClaimValue { unit: String! magnitude: Float! } type NumberClaimValue { value: Float! } type DateTimeClaimValue { value: TimestampTZ! date: Date! time: TimeTZ }

3.3 Updates

An Update represents a change in the asset’s history that links together two versions.

<<>>schema
type Update implements Node { id: ID! @id(t: "update") timestamp: TimestampUTC submittedAt: TimestampUTC! submittedBy: Actor! comment: String result: UpdateResult! }

If the status is ERROR, error will provide a description of the error.

<<>>domain-helpers | schema
enum UpdateStatus { SUCCESS ERROR PENDING }
<<>>domain-helpers | schema
interface UpdateResult { status: UpdateStatus! } type UpdateSuccess implements UpdateResult { status: UpdateStatus! transactionHash: String! resolvedAt: TimestampUTC! asset: Asset changes: [AuditChange!]! } type UpdatePending implements UpdateResult { status: UpdateStatus! } type UpdateError implements UpdateResult { status: UpdateStatus! error: String }

A successful update records the change in claims on the asset under the changes field.

<<>>domain-helpers | schema
union AuditChange = StageSet | QuantitySet | ClaimAdded | ClaimAmended | ClaimDeleted type StageSet { new: AssetStandardStage! } type QuantitySet { new: Quantity! } type ClaimAdded { label: String! new: TypedClaimValue! stage: AssetStandardStage! } type ClaimAmended { label: String! old: TypedClaimValue! new: TypedClaimValue! stage: AssetStandardStage! } type ClaimDeleted { label: String! old: TypedClaimValue! stage: AssetStandardStage! }

3.4 Files

Any actor can upload files to be used as evidence on asset claims.

issuer links to the actor who uploaded the file.

All files are stored in IPFS, and ipfsHash will return the IPFS multihash string of the file in the Geora private IPFS network (such as QmZJ1xT1T9KYkHhgRhbv8D7mYrbemaXwYUkg7CeHdrk1Ye).

To view the file, use the url file. This will generate a temporary link to the file in centralised storage. The link will only last for ten minutes.

<<>>schema
type File implements Node { id: ID! @id(t: "file") issuer: Actor ipfsHash: IPFSHash url: URL mimetype: String! uploadedAt: TimestampUTC! actorRelationships( first: Int last: Int after: Cursor before: Cursor ): FileActorRelationshipConnection @jmf(connection: true) } type FileActorRelationshipConnection implements Connection { pageInfo: PageInfo! edges: [FileActorRelationshipEdge!]! } type FileActorRelationshipEdge { cursor: Cursor! node: Actor! permissions: [FilePermission!]! }

The actorRelationships connection lists actors with one or more permissions on the file. The available permissions are:

SET_PERMISSION

a list of edges

pageInfo

an object which provides pagination information

<<>>schema
type FilePermission { permission: FilePermissionType } enum FilePermissionType { SET_PERMISSION VIEW_FILE USE_FILE }
<<>>domain-helpers | schema
scalar IPFSHash scalar URL

3.5 Asset history

<<>>schema
type AssetHistory { stages: [AssetHistoryStage!]! } type AssetHistoryStage { id: ID! @id(t: "assetStandardStage") name: String! quantity: Quantity claims: [AssetHistoryClaim!]! certificates: [AssetHistoryCertificate!]! attachments: [Attachment!]! } type AssetHistoryClaim { label: String! humanLabel: String! values: [TypedClaimValue!]! } type AssetHistoryCertificate { registry: Registry! actor: Actor! certificate: File }

3.6 Actors

The Actor node provides the actor’s name and auto-generated Ethereum address.

<<>>schema
type Actor implements Node { id: ID! @id(t: "actor") name: String! address: EthereumAddress email: EmailInfo createdAt: TimestampUTC! theme: Theme! plan: SubscriptionPlan! <<domain-actor-methods>> }
<<>>domain-helpers | schema
scalar EthereumAddress

You can access all the assets that an actor has ownership of (i.e. ownershipPercentage > 0) with the assets connection.

<<>>domain-actor-methods | schema
assets( first: Int last: Int after: Cursor before: Cursor ): ActorAssetsConnection @jmf(connection: true)
<<>>domain-helpers | schema
type ActorAssetsConnection implements Connection { pageInfo: PageInfo! edges: [ActorAssetEdge!]! } type ActorAssetEdge implements Edge { cursor: Cursor! node: Asset! }

We can list all the claims of an actor using the claims field.

<<>>domain-actor-methods | schema
claims( first: Int last: Int after: Cursor before: Cursor ): ActorClaimConnection @jmf(connection: true)

Unlike Claims on assets, there is no evidence as yet on ActorClaims.

<<>>schema
type ActorClaim { id: ID! @id(t: "claim") label: String! value: String! }
<<>>domain-helpers | schema
type ActorClaimConnection implements Connection { pageInfo: PageInfo! edges: [ActorClaimEdge!]! } type ActorClaimEdge implements Edge { cursor: Cursor! node: ActorClaim! }

We can get all the registries an actor belongs to, along with their current certification status.

<<>>domain-actor-methods | schema
registries( first: Int last: Int, after: Cursor before: Cursor ): ActorRegistryConnection @jmf(connection: true) @requiresActor
<<>>domain-helpers | schema
type ActorRegistryConnection implements Connection { pageInfo: PageInfo! edges: [ActorRegistryEdge!]! } type ActorRegistryEdge implements Edge { cursor: Cursor! status: CertificationStatus userType: RegistryUserType node: Registry! }

The edge’s status field will be null if the userType is ISSUER.

We can get the certification history of a user.

<<>>domain-actor-methods | schema
history( first: Int last: Int, after: Cursor before: Cursor ): CertificationHistoryConnection @jmf(connection: true) @requiresActor

And their production details.

The actor’s status can be certified, uncertified, or revoked, and each case gives different data. If they’re certified the issuer who granted it, when it was granted, and when it expires is given. If they’re uncertified the time when the last certification expired is given, if they’ve never been certified this is null. If their certification was revoked then the timestamp and the issuer who revoked it is given.

<<>>domain-helpers | schema
union CertificationStatus = Certified | PreCertified | Uncertified | Revoked | Pending type Certified { status: CertificationStatusType! certificate: File user: Actor! issuer: Actor! granted: TimestampUTC! expires: TimestampUTC! } type PreCertified { status: CertificationStatusType! certificate: File user: Actor! issuer: Actor! granted: TimestampUTC! expires: TimestampUTC! } type Uncertified { status: CertificationStatusType! user: Actor! expired: TimestampUTC } type Revoked { status: CertificationStatusType! user: Actor! issuer: Actor! timestamp: TimestampUTC! } type Pending { status: CertificationStatusType! user: Actor! } enum CertificationStatusType { CERTIFIED PRE_CERTIFIED UNCERTIFIED REVOKED PENDING }

Email contact information, and whether the address has been successfully verified.

<<>>domain-helpers | schema
scalar Email type EmailInfo { email: Email! isVerified: Boolean! }

3.7 Networks

Networks are the organising mechanism around which user gather to exchange assets and asset standards. Within a network a manager may issue awards to network members, and roles may be granted to confer permissions to perform certain operations or view certain data on assets.

<<>>schema
type Network { id: ID! @id(t: "network") name: String! description: String theme: Theme! @requiresActor tags: [NetworkTag!]! awards: [Award!]! managers: [NetworkParticipant!]! standards: [AssetStandard!]! <<domain-network-methods>> }

3.7.1 Network Members

We can get the list of members of the network ordered by various fields, such as

<<>>domain-network-methods | schema
members( first: Int last: Int, after: Cursor before: Cursor ): NetworkMemberConnection! @jmf(connection: true orderable: [ {field: "email", orderAs: STRING}, {field: "status", orderAs: STRING}, {field: "contactName", orderAs: STRING}, {field: "businessName", orderAs: STRING}, ] ) @requiresActor

The current awards that the user holds for this network are listed on the edges of the connection.

<<>>domain-helpers | schema
union NetworkParticipant = ParticipantAccepted | ParticipantRejected | ParticipantInvited type ParticipantAccepted { actor: Actor! email: String! contactName: String! businessName: String! } type ParticipantRejected { email: String! contactName: String! businessName: String! } type ParticipantInvited { email: String! contactName: String! businessName: String! invitedBy: String! } type NetworkMemberConnection implements Connection { pageInfo: PageInfo! edges: [NetworkMemberEdge!]! } type NetworkMemberEdge implements Edge { cursor: Cursor! tags: [NetworkTag!]! awards: [CurrentAward!]! node: NetworkParticipant! }

3.7.2 Network Tags

<<>>schema
type NetworkTag { id: ID! @id(t: "tag") name: String! }

3.8 Awards

An award may be given by managers of a network to members of the network to reflect that they have met some standard of quality or process, or just as a token of membership to the network.

<<>>schema
type Award { id: ID! @id(t: "award") name: String! description: String! }

Currently issued awards carry the timestamp of when they were given and when they will expire, if they have an expiry.

<<>>schema
type CurrentAward { award: Award! issued: TimestampUTC! expires: TimestampUTC }

3.9 Registries

Registries have issuers and users, issuers manage the certification status of the users. The registry can also be queried for the overall production statistics.

<<>>schema
type Registry { id: ID! @id(t: "registry") name: String! description: String! certificationName: String! theme: Theme! @requiresActor requiredClaims: [String!]! actors( first: Int last: Int, after: Cursor before: Cursor ): RegistryActorConnection @jmf(connection: true orderable: [ {field: "status", orderAs: STRING}, {field: "issued", orderAs: DATE}, {field: "expiry", orderAs: DATE}, {field: "name", orderAs: STRING} ] ) @requiresActor production( first: Int last: Int, after: Cursor before: Cursor ): ProductionConnection @requiresActor }

There are three types of user in a registry. An issuer can issue certificates to users, and users have a certification status. If the current user is an admin user they will have ADMIN access at least to every registry if they don’t already have one of the other two levels.

<<>>domain-helpers | schema
enum RegistryUserType { USER ISSUER ADMIN }
<<>>domain-helpers | schema
type RegistryActorConnection implements Connection { pageInfo: PageInfo! edges: [RegistryActorEdge!]! } type RegistryActorEdge implements Edge { cursor: Cursor! status: CertificationStatus userType: RegistryUserType! node: Actor! }

The edge’s status field will be null if the userType is ISSUER.

The issuer can invite new users to a registry.

<<>>schema
type Invitation { registry: ID! @id(t: "registry"), user: ID! @id(t: "actor") }

3.9.1 Registry Verification

When registries are initially created, they are not verified in the Geora system. Unverified registries have a limit to the number of users that can be created and participate in the registry. Once the registry is verified by Geora, this limit is removed.

<<>>domain-helpers | schema
interface VerificationResult { status: Boolean! } type Verified implements VerificationResult { status: Boolean! verifier: Actor! timestamp: TimestampUTC! comment: String! } type Unverified implements VerificationResult { status: Boolean! }

3.10 Asset Lenses

An asset lens is a view / set of permissions which can be applied to any number of assets. Each lens has a name and a description; the user can choose to show asset’s class, value, and ownership information. Optionally, time bounds on asset events can be set, and a list of claims which will be shown through the lens.

<<>>schema
type CapabilityDefinition { id: ID! @id(t: "capabilityDefinition") name: String! description: String! createdBy: Actor! read: ReadPermissions! write: WritePermissions! } type ReadPermissions { showOwnership: Boolean! claimsWhitelist: [ReadClaimApproval!]! } type WritePermissions { stageWhitelist: [WriteStageApproval!]! claimsWhitelist: [WriteClaimApproval!]! relationshipsWhitelist: [WriteRelationshipApproval!]! } type ReadClaimApproval { stage: AssetStandardStage # null = any stage label: String # null = any label showEvidence: Boolean # default false } type WriteClaimApproval { stage: AssetStandardStage # null = any stage label: String # null = any label } type WriteStageApproval { stage: AssetStandardStage } type WriteRelationshipApproval { stage: AssetStandardStage # null = any stage relationship: AssetRelationshipType # null = any rel } type Capability { id: ID! @id(t: "capability") url: String! embedCode: String! definition: CapabilityDefinition! theme: Theme! views: Int! }

The claimsWhitelist has some unique behaviour when creating or updating an lens.

When creating with lensCreate, not specifying the claimsWhitelist means that the lens will not show any claims on this asset or, in other words, the claimsWhitelist is empty.

When updating with lensUpdate, not specifying the claimsWhitelist will leave the whitelist untouched. This has 2 consequences:

<<>>schema
input CapabilityDefinitionInput { name: String! description: String! read: ReadPermissionsInput! write: WritePermissionsInput! } input ReadPermissionsInput { showOwnership: Boolean! claimsWhitelist: [ReadClaimApprovalInput!]! } input WritePermissionsInput { stageWhitelist: [ID!]! claimsWhitelist: [WriteClaimApprovalInput!]! relationshipsWhitelist: [WriteRelationshipApprovalInput!]! } input ReadClaimApprovalInput { stage: ID # null = any stage label: String # null = any label showEvidence: Boolean # default false } input WriteClaimApprovalInput { stage: ID # null = any stage label: String # null = any label } input WriteRelationshipApprovalInput { stage: ID # null = any stage relationship: AssetRelationshipType # null = any rel }

When a lens has been created, it can be applied to one or more assets to create lens instances. When viewing these instances, the lens filters out the information visible on the asset. A lens can be deactivated and reactivated, and we keep track of various statistics around people using the lens. Finally, visitors can leave messages for the owner of the lens to read, and they can leave a return email address if they wish.

The UpdatesConnection in LensInstances only contains successful updates.

The decision of whether to show claim data is done in a “white-list” fashion, in that only the explicitly approved claims are revealed. If any claims are present that the lens owner failed to anticipate then they will not be visible. For each claim the lens owner can also say whether users should be able to view the evidence files attached to the claim.

3.11 Asset Standards

An asset standard is used to build assets which are compatible across multiple users and their supply chains. It consists of a set of required and optional claims, transformations, and metadata that define the “shape” of an asset.

<<>>schema
type AssetStandardClaim { label: String! required: Boolean! highlighted: Boolean autocomplete: Boolean! humanLabel: String humanDescription: String claimType: ClaimType! exampleValues: [TypedClaimValue!]! defaultValue: TypedClaimValue claimGroup: String enumeration: Enumeration } type AssetStandardStage { id: ID! @id(t: "assetStandardStage") name: String! claims: [AssetStandardClaim!]! representativeClaim: String iconURI: String description: String } type AssetStandard { id: ID! @id(t: "assetStandard") name: String! description: String! createdBy: Actor! usageStatus: AssetStandardUsageStatus! stages: [AssetStandardStage!]! createdAt: TimestampUTC! }

The asset standard usage status enum is useful for filtering and sorting the most relevant standards for a user. IN_USE_BY_CALLER means that the calling actor is the owner of at least one asset that uses this asset standard. CREATED_BY_CALLER means that the calling actor is the creator of the standard. NETWORK means that the standard has been created by a manager in a network that the actor is part of.

<<>>schema
enum AssetStandardUsageStatus { IN_USE_BY_CALLER CREATED_BY_CALLER NETWORK DEFAULT }

The corresponding input types:

<<>>schema
input AssetStandardClaimValueInput { valueText: ClaimInputText valueQuantity: ClaimInputQuantity valueNumber: ClaimInputNumber valueDateTime: ClaimInputDateTime } input AssetStandardClaimInput { label: String! required: Boolean! highlighted: Boolean autocomplete: Boolean humanLabel: String humanDescription: String claimType: ClaimType exampleValues: [AssetStandardClaimValueInput!] defaultValue: AssetStandardClaimValueInput claimGroup: String enumeration: ID } input AssetStandardStageInput { name: String! claims: [AssetStandardClaimInput!] representativeClaim: String iconURI: String description: String } input AssetStandardInput { name: String! description: String! stages: [AssetStandardStageInput!] }

3.11.1 Enumerations

Enumerations allow restricting the values of asset standard claims to a list of choices.

<<>>schema
enum EnumerationExtensibility { OPEN CLOSED } type EnumerationValue { name: String! value: TypedClaimValue! } type Enumeration { id: ID! @id(t: "enumeration") name: String! claimType: ClaimType! values: [EnumerationValue!]! extensibility: EnumerationExtensibility! }

And the input types:

<<>>schema
input EnumerationValueInput { name: String! value: AssetStandardClaimValueInput! } input EnumerationInput { name: String! claimType: ClaimType! values: [EnumerationValueInput!]! }

3.12 Integrations

<<>>schema
type Integration { id: ID! name: String! status: IntegrationStatusInfo! } enum IntegrationStatus { INTEGRATION_UNAVAILABLE INTEGRATION_AVAILABLE INTEGRATION_ACTIVE } interface IntegrationStatusInfo { status: IntegrationStatus! } type IntegrationAvailable implements IntegrationStatusInfo { status: IntegrationStatus! activationUrl: URL } type IntegrationUnavailable implements IntegrationStatusInfo { status: IntegrationStatus! requiresPlan: SubscriptionPlan! } type IntegrationActive implements IntegrationStatusInfo { status: IntegrationStatus! }

4 Queries

GraphQL queries read data from the API.

Singular queries return an object by its ID. They work the same as the Node query but don’t require fragment matching on returned type. If the caller does not have permission to view the object, the query will return null and an error.

Plural queries return a Connection.

<<>>schema
<<query-helpers>> scalar TypedID type Query { <<query-queries>> }

4.1 Asset

The asset query will return a single asset, if the caller has permission to view the asset.

The assets query will return all assets for which the caller has permission to view.

<<>>query-queries | schema
asset(id: ID!): Asset @requiresActorOrCapability @jm assets( first: Int last: Int after: Cursor before: Cursor ): AssetsConnection! @requiresActorOrCapability @jm(connection: true, orderable: [ { field: "batch_id", orderAs: STRING }, { field: "stage_id", orderAs: STRING } ], query: true)
<<>>query-helpers | schema
type AssetsConnection implements Connection { pageInfo: PageInfo! edges: [AssetEdge!]! } type AssetEdge implements Edge { cursor: Cursor! node: Asset! }

4.2 Actor

The actor query will return a single actor.

The actors query will return all actors that were created by the calling user.

<<>>query-queries | schema
actor(id: ID!): Actor @jm @requiresActor actors( first: Int last: Int after: Cursor before: Cursor ): ActorsConnection! @jm( connection: true, orderable: [{field: "name", orderAs: STRING}], query: true ) @requiresActor
<<>>query-helpers | schema
type ActorsConnection implements Connection { pageInfo: PageInfo! edges: [ActorEdge!]! } type ActorEdge { cursor: Cursor! node: Actor! }

4.3 Update

The update query will return a single update if the caller can view that update.

The updates query will return all updates that a caller can view.

A calling actor can view an update if either of the following conditions are met:

<<>>query-queries | schema
update(id: ID!): Update @jm(query: true) @requiresActor

4.3.1 History

The history of the user’s certification status is of interest, and can be fully queried. Any event such as granting, revoking, or extending certification is revealed here.

<<>>schema
type CertificationHistory { event: CertificationHistoryEvent! registry: Registry! issuer: Actor! notes: String! timestamp: TimestampUTC! }
<<>>domain-helpers | schema
enum CertificationHistoryEvent { CERTIFICATION_GRANTED CERTIFICATION_EXPIRED CERTIFICATION_EXTENDED CERTIFICATION_REVOKED }
<<>>domain-helpers | schema
type CertificationHistoryConnection implements Connection { pageInfo: PageInfo! edges: [CertificationHistoryEdge!]! } type CertificationHistoryEdge implements Edge { cursor: Cursor! node: CertificationHistory! }

4.3.2 Production

The quantity in units and weight of TTO assets produced can be queried, aggregating over various time periods as given by Granularity, grouped by certification status.

<<>>schema
type Production { dateFrom: Date! dateTo: Date! assetsCertified: Int! assetsUncertified: Int! assetsRevoked: Int! kgCertified: Float! kgUncertified: Float! kgRevoked: Float! kgSold: Float! kgPurchased: Float! totalInventory: Float! }
<<>>domain-helpers | schema
enum Granularity { PER_WEEK PER_MONTH }

The where defaults are from the beginning of time up to the current time, and PER_DAY granularity.

<<>>domain-helpers | schema
type ProductionConnection implements Connection { pageInfo: PageInfo! edges: [ProductionEdge!]! } type ProductionEdge implements Edge { cursor: Cursor! node: Production! }

4.4 Notifications

Give system users access to notifications addressed to them. A message may not have a from field if it has come from an user unknown to the user. The metaData field is a JSONified string which can be an empty JSON object.

<<>>schema
type Notification { id: ID! @id(t: "notification") from: Actor message: String! metaData: String! timestamp: TimestampUTC! seen: Boolean! }
<<>>query-queries | schema
notifications( first: Int last: Int, after: Cursor before: Cursor ): NotificationConnection! @jm(connection: true, query: true) @requiresActor
<<>>domain-helpers | schema
type NotificationConnection implements Connection { pageInfo: PageInfo! edges: [NotificationEdge!]! } type NotificationEdge implements Edge { cursor: Cursor! node: Notification! }

We can set notifications as having been seen by the user so they won’t keep seeing the same ones.

<<>>mutation-methods | schema
setNotificationsSeen( ids: [ID!]! ): SetNotificationsSeenResult! @f(name: "set_notifications_seen") @requiresActor
<<>>domain-helpers | schema
type SetNotificationsSeenResult { updated: [ID!]! }

4.5 File

The file query will return a single file if the caller has the VIEW_FILE permission.

The files query will return all files for which the caller has the VIEW_FILE permission.

<<>>query-queries | schema
file(id: ID!): File @jm @requiresActor files( first: Int last: Int after: Cursor before: Cursor ): FilesConnection! @jm(connection: true, query: true) @requiresActor
<<>>query-helpers | schema
type FilesConnection implements Connection { pageInfo: PageInfo! edges: [FileEdge!]! } type FileEdge implements Edge { cursor: Cursor! node: File! }

4.6 Networks

<<>>query-queries | schema
network(id: ID!): Network @jm @requiresActor networks( first: Int last: Int, after: Cursor before: Cursor ): NetworkConnection! @jm(connection: true, orderable: [ {field: "name", orderAs: STRING}, {field: "userType", orderAs: STRING} ], query: true) @requiresActor

The current actor may be a manager or a member of a network, but never both.

<<>>domain-helpers | schema
enum NetworkUserType { MEMBER MANAGER }
<<>>domain-helpers | schema
type NetworkConnection implements Connection { pageInfo: PageInfo! edges: [NetworkEdge!]! } enum NetworkMemberStatus { ACCEPTED INVITED REJECTED } type NetworkEdge implements Edge { cursor: Cursor! userType: NetworkUserType! joinedAt: TimestampUTC! status: NetworkMemberStatus! node: Network! profilePrivacy: ProfilePrivacy! }

4.7 Registries

<<>>query-queries | schema
registry(id: ID!): Registry @jm @requiresActor registries( first: Int last: Int, after: Cursor before: Cursor ): RegistryConnection! @jm(connection: true, orderable: [ {field: "status", orderAs: STRING}, {field: "expiry", orderAs: DATE}, {field: "name", orderAs: STRING} ], query: true) @requiresActor
<<>>domain-helpers | schema
type RegistryConnection implements Connection { pageInfo: PageInfo! edges: [RegistryEdge!]! } type RegistryEdge implements Edge { cursor: Cursor! status: CertificationStatus userType: RegistryUserType! node: Registry! }

The edge’s status field will be null if the userType is ISSUER.

4.8 Lenses

<<>>query-queries | schema
capabilityDefinition(id: ID!): CapabilityDefinition! @jm @requiresActor @alpha capabilityDefinitions( first: Int last: Int, after: Cursor before: Cursor ): CapabilityDefinitionConnection! @jm(connection: true, query: true) @requiresActor @alpha capability(id: ID!): Capability! @jm @requiresActorOrCapability @alpha
<<>>domain-helpers | schema
type CapabilityDefinitionConnection implements Connection { pageInfo: PageInfo! edges: [CapabilityDefinitionEdge!]! } type CapabilityDefinitionEdge implements Edge { cursor: Cursor! node: CapabilityDefinition! }

4.9 Asset standards

The assetStandard query will return a single asset standard.

The assetStandards query will return all asset standards and their status. It can be sorted and filtered by status to return only those standards in use or created by the caller.

<<>>query-queries | schema
assetStandard(id: ID!): AssetStandard @jm @requiresActor @alpha assetStandards( first: Int last: Int after: Cursor before: Cursor ): AssetStandardConnection! @jm( connection: true, orderable: [{field: "name", orderAs: STRING}, {field: "usageStatus", orderAs: STRING}], query: true ) @requiresActor @alpha
<<>>domain-helpers | schema
type AssetStandardConnection implements Connection { pageInfo: PageInfo! edges: [AssetStandardEdge!]! } type AssetStandardEdge implements Edge { cursor: Cursor! node: AssetStandard! }

4.10 Enumerations

<<>>query-queries | schema
enumeration(id: ID!): Enumeration @jm @requiresActor @alpha

4.11 Integrations

<<>>query-queries | schema
integration(id: ID!): Integration! @jm @requiresActor @alpha integrations( first: Int last: Int after: Cursor before: Cursor ): IntegrationConnection! @jm(connection: true, query: true) @requiresActor @alpha
<<>>domain-helpers | schema
type IntegrationConnection implements Connection { pageInfo: PageInfo! edges: [IntegrationEdge!]! } type IntegrationEdge implements Edge { cursor: Cursor! node: Integration! }

4.12 Filtering and ordering connections

Coming soon.

<<>>schema
input WhereClauseString { _eq: String _neq: String _gt: String _gte: String _lt: String _lte: String _like: String } input WhereClauseID { _eq: ID _neq: ID _gt: ID _gte: ID _lt: ID _lte: ID } input WhereClauseInt { _eq: Int _neq: Int _gt: Int _gte: Int _lt: Int _lte: Int } input WhereClauseFloat { _eq: Float _neq: Float _gt: Float _gte: Float _lt: Float _lte: Float } input WhereClauseBoolean { _eq: Boolean _neq: Boolean } enum OrderBy { ASCENDING DESCENDING } input Orderable { field: String! orderAs: OrderAs! } enum OrderAs { STRING FLOAT DATE }

5 Mutations

Mutations make updates to the system state. All mutations are atomic.

<<>>schema
<<mutation-helpers>> type Mutation { <<mutation-methods>> }

5.1 Users

To reset the calling user’s API key and return a new autogenerated key, use userResetKey.

<<>>mutation-methods | schema
userResetKey: String!

5.2 Actors

You can create an actor by providing a name, and update them by changing their name. The actorCreate mutation is the only mutation or query that doesn’t require a calling actor to be specified with the x-geora-actor header key.

<<>>mutation-methods | schema
actorCreate(input: ActorCreateInput!): Actor @jm(idType: "UUID") actorUpdate(id: ID!, input: ActorUpdateInput!): Actor @jm(idType: "UUID") @requiresActor
<<>>mutation-helpers | schema
input ActorCreateInput { name: String! claims: [ActorClaimInput!] registries: [ID!] } input ActorUpdateInput { name: String claims: [ActorClaimInput!] }

Claims which are being provided to can have absent or null ids, which means a claim with the label given is expected to not yet exist on the actor. If such a claim does exist an error is thrown. If the id given does not match the current latest version id then an error is also thrown.

An input claim can also have a null value, in which case the claim will be deleted if the provided id is the latest id.

<<>>mutation-helpers | schema
input ActorClaimInput { id: ID label: String! value: String }

If you are the issuer of a registry, and want to invite new users to join Geora as part of your registry, you can use the actorCreatePending mutation.

<<>>mutation-methods | schema
actorCreatePending(input: ActorCreatePendingInput!): Boolean
<<>>mutation-helpers | schema
input ActorCreatePendingInput { name: String! email: String! claims: [ActorClaimInput!] registries: [ID!] }

5.3 Assets

Assets are created and updated using the flexible AssetUpdateInput type.

<<>>mutation-methods | schema
assetCreate(input: AssetCreateInput!): Update @jm @requiresActor assetUpdate(id: ID!, input: AssetUpdateInput!): Update @jm @requiresActor
<<>>mutation-helpers | schema
input AssetCreateInput { assetStandard: ID! stage: ID! quantity: [QuantityInput!] claims: [ClaimInput!] actorRelationships: [ActorRelationshipInput!] assetRelationships: [AssetRelationshipInput!] comment: String timestamp: TimestampUTC } input AssetUpdateInput { assetStandard: ID stage: ID quantity: [QuantityInput!] claims: [ClaimInput!] actorRelationships: [ActorRelationshipInput!] assetRelationships: [AssetRelationshipInput!] comment: String timestamp: TimestampUTC } input ActorRelationshipInput { id: ID! exactOwnershipPercentage : Decimal } input AssetRelationshipInput { id: ID! stage: ID! relationship: AssetRelationshipInputType! }

Calling any mutation that updates an Asset will return an Update, with an initial status of pending. When the update has been processed, its status will change to successful.

When creating an asset, any omitted field will receive the default values.

When updating the asset, any omitted field will be unchanged in the update. Updating assets using the assetUpdate mutation allows you to make multiple changes in the same update, but performs replacement on the provided collections. For example, providing an actorRelationships array in assetUpdate will replace all existing relationships. If this is not desired, use the more granular update functions below.

Any mutations to an asset are rejected if the asset status is BURNED.

QuantityInput is used to ensure that both quantity fields are set together or not at all.

<<>>mutation-helpers | schema
input QuantityInput { primaryValue: Float! primaryUnit: String! stage: ID }

If an assetStandard is specified, but the asset does not conform to that standard, the update will be rejected.

5.4 Asset relationships

These functions provide finer-grained editing for relationships.

assetPut* will update any existing relationships from the input to match the input, and create any new relationships.

<<>>mutation-methods | schema
assetPutActorRelationships( id: ID! input: [ActorRelationshipInput!]! comment: String ): Update @jm @requiresActor assetPutAssetRelationships( id: ID! input: [AssetRelationshipInput!]! comment: String ): Update @jm @requiresActor

assetRemove* will remove all the specified relationships.

<<>>mutation-methods | schema
assetRemoveActorRelationships( id: ID!, actors: [ID!]! comment: String ): Update @jm @requiresActor assetRemoveAssetRelationships( id: ID! relationships: [AssetRelationshipInput!]! comment: String ): Update @jm @requiresActor

Note that all of the above will create a new Update.

The assetAddToBulkCollection mutation supports bulk “in-turns” to a silo or other container. The mutation:

  1. Adds a new asset relationship between the given asset and the collection asset

If transfer is true, the mutation also:

  1. Increases the quantity of the collection by the quantity of the added asset
  2. Burns the given asset

If transfer is true, this mutation will fail if the asset’s primary unit does not match that of the collection.

The assetRemoveFromBulkCollection mutation does the opposite, removing the relationship and decreasing the collection quantity.

The Update returned is for the collection asset.

5.5 Claims

assetPutClaims will update any existing claims to match the input, and create any new claims.

<<>>mutation-helpers | schema
input ClaimInputText { value: String! } input ClaimInputQuantity { magnitude: Float! unit: String! } input ClaimInputNumber { value: Float! } input ClaimInputDateTime { value: TimestampTZ date: Date time: TimeTZ } input ClaimInput { label: String! value: String stage: ID valueText: ClaimInputText valueQuantity: ClaimInputQuantity valueNumber: ClaimInputNumber valueDateTime: ClaimInputDateTime evidence: [ID!] }
<<>>mutation-methods | schema
assetPutClaims( id: ID!, input: [ClaimInput!]!, comment: String ): Update @jm @requiresActor

5.6 Burning an asset

assetBurn will change the status of the asset to BURNED. This is a one-way operation.

<<>>mutation-methods | schema
assetBurn(id: ID!): Update @jm @requiresActor

5.7 Uploading a file

The fileCreate mutation will upload and create a new file.

<<>>mutation-methods | schema
fileCreate(file: Upload!): File @jm(idType: "UUID") @requiresActor

Upload is a special type which receives a file from the request body’s multipart form data.

This means that the mutation must be called in a different way than the other mutations. Instead of taking a JSON query or mutation as the body, a form data object must be sent, containing three fields:

To make this more concrete, a valid fileCreate operation to upload blah.txt with curl would look like:

curl "http://localhost:3131/v2" \
    -F operations='{"query":"mutation($file: Upload!) {fileCreate(file:$file) {id}}","variables":{"file":null}}' \
    -F map='{ "0": ["variables.file"] }' \
    -F 0=@'blah.txt' \
    -H 'x-api-user: 50d5463a-f4f1-42e3-9ebb-c20171ea0896' \
    -H 'x-api-key: my-secret-key' \
    -H 'x-geora-actor: YWN0b3J8NTBkNTQ2M2EtZjRmMS00MmUzLTllYmItYzIwMTcxZWEwODk2Cg=='

The map field links the file sent in the form data with the mutation’s file variable.

5.8 File relationships

These functions provide finer-grained editing for file relationships.

<<>>mutation-helpers | schema
input FileActorRelationshipInput { id: ID! permissions: [FilePermissionInput!] } input FilePermissionInput { permission: FilePermissionType! }

filePut* will update any existing relationships from the input to match the input, and create any new relationships.

<<>>mutation-methods | schema
filePutActorRelationships( fileID: ID! input: [FileActorRelationshipInput!]! ): File @requiresActor

fileRemove* will remove all the specified relationships.

<<>>mutation-methods | schema
fileRemoveActorRelationships( fileID: ID!, actors: [ID!]! ): File @requiresActor

5.9 Bridging an asset

assetBridge allows moving a Geora asset to another blockchain. Currently, we support bridging to the Ethereum mainnet. This is a one-way operation, and after bridging you will no longer be able to edit the Geora asset.

<<>>domain-helpers | schema
input AssetBridgeInput { ethereum: AssetBridgeInputEthereum! } input AssetBridgeInputEthereum { toAddress: EthereumAddress! }
<<>>mutation-methods | schema
assetBridge( id: ID!, input: AssetBridgeInput! ): Update! @requiresPayingUser(plan: PRO) @requiresActor

The chain field on the Asset type will provide details of the asset after bridging.

<<>>domain-helpers | schema
union Chain = GeoraChain | EthereumChain enum AssetChain { GEORA ETHEREUM } type GeoraChain { chain: AssetChain! } type EthereumChain { chain: AssetChain! transactionHash: String! }

5.10 Networks

Creating a network requires only a name. The caller will become the first manager of the network.

<<>>domain-helpers | schema
input NetworkCreateInput { name: String! description: String } input NetworkInvitationCreateInput { businessName: String! contactName: String! email: String! tags: [ID!] userType: NetworkUserType! } input NetworkTagInput { name: String! }
<<>>mutation-methods | schema
networkCreate( input: NetworkCreateInput! ): Network @jm @requiresActor networkInvitationCreate(id: ID!, input: NetworkInvitationCreateInput!): Boolean! @requiresActor networkInvitationAccept(id: ID!): Boolean! @requiresActor networkInvitationReject(id: ID!): Boolean! @requiresActor networkTagCreate(id: ID!, input: NetworkTagInput!): NetworkTag! @requiresActor

5.10.1 Profile visibility

A member can change their network profile visibility with the networkProfilePrivacySet mutation.

When PUBLIC, a member is visible to other members in the network, and can be traded with.

When PRIVATE, a member is only visible to network managers.

<<>>domain-helpers | schema
enum ProfilePrivacy { PUBLIC PRIVATE }
<<>>mutation-methods | schema
networkProfilePrivacySet(id: ID!, privacy: ProfilePrivacy!): ProfilePrivacy! @requiresActor

5.11 Registries

A registry can have different behaviour for passing certificates along a chain of ownership.

<<>>domain-helpers | schema
enum CertificatesRequiredToContinue { NONE ONE ALL }

Anyone can create a new registry and become its first issuer.

<<>>domain-helpers | schema
input RegistryCreateInput { name: String!, description: String!, certificationName: String!, ipfsHashOfLogo: String, ipfsHashOfDocumentation: String, requiredClaims: [String!]!, userUniquenessClaim: String!, isPreCertifiable: Boolean!, certificatesRequiredForContinuation: CertificatesRequiredToContinue! }

ipfsHashOfLogo has been deprecated - see Theme API.

<<>>mutation-methods | schema
registryCreate( input: RegistryCreateInput! ): Registry @jm(idType: "TEXT") @requiresActor
<<>>mutation-methods | schema
grantCertification( registry: ID!, user: ID!, begins: TimestampUTC!, expires: TimestampUTC!, fileID: ID!, notes: String! ): Actor @jm(idType: "UUID") @requiresActor extendCertification( registry: ID!, user: ID!, expires: TimestampUTC!, notes: String! ): Actor @jm(idType: "UUID") @requiresActor revokeCertification( registry: ID!, user: ID!, notes: String! ): Actor @jm(idType: "UUID") @requiresActor
<<>>domain-helpers | schema
input InvitationCreateInput { registry: ID!, role: RegistryUserType!, userID: ID, userEmail: String }
<<>>mutation-methods | schema
invitationCreate( input: InvitationCreateInput! ): InvitationCreateResult @requiresActor
<<>>domain-helpers | schema
union InvitationCreateResult = InvitationCreateSuccess | InvitationCreateFailure type InvitationCreateSuccess { successful: Boolean! invitation: Invitation! } type InvitationCreateFailure { successful: Boolean! reason: String! }

The recipient of an invitation can accept or reject it.

<<>>domain-helpers | schema
input InvitationAcceptInput { registry: ID! } input InvitationRejectInput { registry: ID! }
<<>>mutation-methods | schema
invitationAccept( input: InvitationAcceptInput! ): Invitation @requiresActor invitationReject( input: InvitationRejectInput! ): Invitation @requiresActor

If for some reason an asset fails automatic certification, you can edit and resubmit it.

<<>>mutation-methods | schema
assetSubmitForCertification( versionID: ID! registries: [ID!]! ): Asset! @jm(id: "asset_version_id") @requiresActor

5.12 Lenses

Lenses can be created and updated given a definition.

<<>>mutation-methods | schema
capabilityDefinitionCreate(input: CapabilityDefinitionInput!): CapabilityDefinition! @jm @requiresActor @beta capabilityDefinitionUpdate(id: ID!, input: CapabilityDefinitionInput!): CapabilityDefinition! @jm @requiresActor @beta

Given an existing lens, the lens can be “instantiated” to apply it to an asset very easily. The underlying lens is shared between all assets it is applied to so that an update to the lens affects all instances as well.

<<>>mutation-methods | schema
capabilityGrant(id: ID!, asset: ID!, actor: ID): Capability! @jm(idType: "UUID") @requiresActor @beta
<<>>mutation-methods | schema
capabilityRevoke(id: ID!, asset: ID!, actor: ID): Boolean! @requiresActor @beta

5.13 Asset standards

Asset standards can be created and updated.

An asset standard can only be updated by its creator.

<<>>mutation-methods | schema
assetStandardCreate( input: AssetStandardInput! ): AssetStandard! @jm @requiresActor @alpha assetStandardUpdate( id: ID!, input: AssetStandardInput! ): AssetStandard! @jm @requiresActor @alpha

Individual stages and claims on the asset standard can be updated independently.

<<>>mutation-methods | schema
assetStandardStageUpdate( id: ID!, stageName: String!, input: AssetStandardStageInput! ): AssetStandard! @jm @requiresActor @alpha assetStandardClaimUpdate( id: ID!, stage: ID!, input: AssetStandardClaimInput! ): AssetStandard! @jm @requiresActor @alpha

When using assetStandardClaimUpdate, if the claim input has the same label as a claim that exists on that stage, the claim will be edited in-place. Otherwise, a new claim will be added to the stage.

When updating claims in asset standards, the new claims must be compatible with assets created with a previous revision of the standard. This means that new claims must either be optional or have a default value. The human-readable label and description can be changed at any time.

5.13.1 Enumerations

Enumerations allow restricting the values of asset standard claims to a list of choices.

<<>>mutation-methods | schema
enumerationCreate(input: EnumerationInput!): Enumeration @jm @requiresActor @alpha

5.14 Theming

Each registry has its own theme attached. There is a default theme provided by Geora which can be configured by a registry issuer.

A theme consists of a logo url and colour scheme.

The ColorHex scalar is a hexadecimal string representing a valid CSS color. For example #e35b2f or #f46f56. Note that the ‘#’ should be included in the string.

<<>>domain-helpers | schema
type Theme { logoUrl: String! colorScheme: ColorScheme! isDefault: Boolean! } type ColorScheme { colorPrimary: ColorHex! colorSecondary: ColorHex! colorDanger: ColorHex! colorWarning: ColorHex! colorSuccess: ColorHex! colorSidebarBackground: ColorHex! colorSidebarForeground: ColorHex! colorSidebarForegroundDark: ColorHex! colorSidebarHoverDark: ColorHex! colorSidebarHoverLight: ColorHex! colorSidebarActiveDot: ColorHex! } scalar ColorHex

The theme of a registry can be queried by any actor (issuer or user) in the registry.

The theme of a registry can be updated only by the issuer of a registry.

<<>>mutation-helpers | schema
input ColorSchemeInput { colorPrimary: ColorHex colorSecondary: ColorHex colorDanger: ColorHex colorWarning: ColorHex colorSuccess: ColorHex colorSidebarBackground: ColorHex colorSidebarForeground: ColorHex colorSidebarForegroundDark: ColorHex colorSidebarHoverDark: ColorHex colorSidebarHoverLight: ColorHex colorSidebarActiveDot: ColorHex } input SetRegistryThemeInput { registryId: ID! logoUrl: String colorScheme: ColorSchemeInput } input ActorSetThemeInput { actorId: ID! logoUrl: String colorScheme: ColorSchemeInput }

Note that fields in the ColorSchemeInput are not compulsory. This is because each colour has a default value which will be applied if no value is given.

<<>>mutation-methods | schema
setRegistryTheme( input: SetRegistryThemeInput! ): Theme @jm(idType: "text", id: "entity_id") @requiresPayingUser(plan: STARTER) @requiresActor actorSetTheme( input: ActorSetThemeInput! ): Theme @jm(idType: "text", id: "entity_id") @requiresPayingUser(plan: STARTER) @requiresActor

6 Subscriptions

To support instantaneous information when an event has been processed by the blockchain, the Geora API uses GraphQL Subscriptions to allow clients to connect and receive events from the server.

6.1 Update

Mutations performed in the Geora API are asynchronous. Most mutations return an Update object which remains in a PENDING state until the blockchain has processed the information.

The API supports subscriptions for Update objects. Whenever a user’s Update changes from the PENDING state, an event is emitted to all listeners with the correct permissions.

<<>>schema
type Subscription { update(id: ID): Update }

Clients can subscribe to a particular Update as below.

// In the graphql playground

subscription UpdateStatus {
    update(id: "dXBkYXRlfDBiMDY2MDllLWMwZmMtNGYzYS05NWQ3LTQzYzNjYmRlZDNiYg==") {
        result {
            status
        }
    }
}

If the id argument is omitted, the user will be subscribed to to all updates they have permission to view.

subscription UpdateStatus {
    update {
        result {
            status
        }
    }
}

7 Errors

When a query or mutation fails, the returned JSON body will contain an errors field.

For example, the following JSON is a failed request to the assetUpdate mutation, where the calling actor does not exist.

{
  "errors": [
    {
      "message": "Could not find actor with ID YWN0b3J8NGI3YjFkMjAtZWM3Yi00YTI5LWI5OGEtZDJiMmRiNjhhMTM1",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "assetUpdate"
      ],
      "extensions": {
        "code": "FORBIDDEN",
      }
    }
  ],
  "data": {
    "assetUpdate": null
  }
}

For each error, there will be a message provided explaining the error, as well as a code to help categorise errors. Note that the expected return data is null when an error occurs.