GREENGAGE API

Overview

The GREENGAGE API is a GraphQL-based API that provides access to all functionality of the GREENGAGE platform. It serves as the backend for the GREENGAGE mobile apps and can be used for custom integrations.

API Endpoint: https://api.greengage.dev/graphql

Technology: Apollo Server (GraphQL)

Important Naming Convention

!!! warning "City vs Observatory" In the API, Observatories are referred to as "City" (e.g., city, cities, updateFavoriteCity). This naming exists for historical reasons and has been retained for backwards compatibility. When you see "City" in the API, it refers to a Citizen Observatory.

Authentication

The API supports multiple authentication methods:

Method Description
Email/Password Register and login with email credentials
Keycloak OAuth-based authentication via Keycloak
Apple/Google Social login (handled via Keycloak)

Getting a Token

mutation Login {
  login(email: "user@example.com", password: "yourpassword") {
    token
    refresh_token
  }
}

Token Lifetime

!!! info "Token Expiration" The access token has a short lifetime of 5 minutes for security reasons. Use the refresh token to obtain a new access token when it expires.

Token Type Lifetime Purpose
Access Token 5 minutes Used for all API requests
Refresh Token 30 days Used only to obtain new access tokens

Refreshing Tokens

When your access token expires, use the refresh token to get a new one:

mutation RefreshToken($token: String!) {
  refreshToken(token: $token) {
    token
  }
}

Refresh Token

!!! warning "Security Best Practice" Store tokens securely. Never save them in local storage for web applications. Use secure HTTP-only cookies or secure storage mechanisms instead.

Using the Token

Include the token in the Authorization header:

Authorization: Bearer <your-token>

GraphQL Schema

Core Types

City (Observatory)

Represents a Citizen Observatory where citizens can participate.

type City {
  id: String!
  name: String
  geo_start: GeoPosition
  geo_stop: GeoPosition
  grid: TilePaginationList
  missionsList: MissionList
  joinable: Boolean
}

User

type User {
  id: ID!
  firstname: String
  lastname: String
  displayname: String
  email: String
  phone: String
  city: City              # The user's current Observatory
  avatar: FairuAsset
  language: String
  score: Int
  badges: [UserBadge]
  created_at: String
  updated_at: String
}

Mission

Missions are tasks that citizens can complete within an Observatory.

type Mission {
  id: ID!
  title: String
  description: String
  asset: FairuAsset
  provider: MissionProvider
  type: MissionType
  duration: Int           # Estimated duration in minutes
  latitude: Float
  longitude: Float
  geojson: GeoJSON
  active: Boolean
  status: MissionStatus
  points: Int
  survey: Survey
  poi: PointOfInterest
  topic: Topic
  completed: Boolean
  created_at: String
  updated_at: String
}

PointOfInterest (POI)

type PointOfInterest {
  id: ID!
  title: String
  description: String
  asset: FairuAsset
  type: PointOfInterestType
  topic: Topic
  latitude: Float
  longitude: Float
  missions: [Mission]
}

Spot

User-created observations on the map.

type Spot {
  id: ID!
  title: String
  description: String
  latitude: Float
  longitude: Float
  topic: Topic
  asset: FairuAsset
  created_at: String
}

Survey

type Survey {
  id: ID!
  title: String
  description: String
  elements: [SurveyElement]
}

Topic

Topics/Themes for categorizing content.

type Topic {
  id: ID!
  title: String
  color: String
}

Queries

User Queries

# Get current authenticated user
query Me {
  me {
    id
    displayname
    email
    city {
      id
      name
    }
    score
  }
}

Observatory Queries

# Get all available Observatories
query GetObservatories {
  cities {
    id
    name
    joinable
    geo_start {
      latitude
      longitude
    }
  }
}

# Get a specific Observatory
query GetObservatory($id: String!) {
  city(id: $id) {
    id
    name
    missionsList {
      missions {
        id
        title
        type
      }
    }
  }
}

Mission Queries

# Get all missions for an Observatory
query GetMissions($cityId: String!) {
  missions(city_id: $cityId) {
    id
    title
    description
    type
    status
    points
    poi {
      title
      latitude
      longitude
    }
  }
}

# Get user's active missions
query MyMissions {
  myMissions {
    id
    title
    status
    completed
  }
}

# Get a specific mission
query GetMission($id: ID!) {
  mission(id: $id) {
    id
    title
    description
    survey {
      elements {
        type
        label
      }
    }
  }
}

POI Queries

# Get POIs for an Observatory
query GetPOIs($cityId: String!) {
  pois(city_id: $cityId) {
    id
    title
    description
    latitude
    longitude
    topic {
      title
      color
    }
    missions {
      id
      title
    }
  }
}

Spot Queries

# Get spots for an Observatory
query GetSpots($cityId: String!) {
  spots(city_id: $cityId) {
    id
    title
    description
    latitude
    longitude
    topic {
      title
    }
  }
}

Mutations

Authentication

# Register new account
mutation Register($email: String!, $password: String!, $firstname: String, $lastname: String) {
  register(email: $email, password: $password, firstname: $firstname, lastname: $lastname) {
    token
    refresh_token
  }
}

# Login
mutation Login($email: String!, $password: String!) {
  login(email: $email, password: $password) {
    token
    refresh_token
  }
}

# Keycloak login
mutation KeycloakLogin($token: String!) {
  keycloak(token: $token) {
    token
    refresh_token
  }
}

# Refresh token
mutation RefreshToken($refresh_token: String!) {
  refreshToken(refresh_token: $refresh_token) {
    token
    refresh_token
  }
}

# Reset password
mutation ResetPassword($email: String!) {
  resetPassword(email: $email)
}

# Delete account
mutation DeleteAccount {
  deleteAccount
}

Profile Management

# Update profile
mutation UpdateProfile($firstname: String, $lastname: String, $language: String) {
  updateProfile(firstname: $firstname, lastname: $lastname, language: $language) {
    id
    displayname
  }
}

# Change Observatory
mutation ChangeObservatory($city_id: String!) {
  updateFavoriteCity(city_id: $city_id)
}

# Join an Observatory
mutation JoinObservatory($id: String!) {
  joinObservatory(id: $id) {
    id
    name
  }
}

Mission Operations

# Start a mission
mutation StartMission($id: ID!) {
  startMission(id: $id) {
    id
    status
  }
}

# Stop/Cancel a mission
mutation StopMission($id: ID!) {
  stopMission(id: $id) {
    id
    status
  }
}

# Validate a mission
mutation ValidateMission($id: ID!, $valid: Boolean!) {
  validateMission(id: $id, valid: $valid) {
    id
    status
  }
}

# Add data to a mission
mutation AddMissionData($mission_id: ID!, $data: AddMissionDataset!) {
  addMissionDataset(mission_id: $mission_id, data: $data) {
    id
  }
}

Survey Submission

# Submit survey answers
mutation SubmitSurvey($survey_id: ID!, $answers: [SurveyAnswerInput!]!) {
  submitToSurvey(survey_id: $survey_id, answers: $answers) {
    id
  }
}

Spot Operations

# Create a new spot
mutation CreateSpot($input: CreateSpot!) {
  createSpot(input: $input) {
    id
    title
    latitude
    longitude
  }
}

# Update a spot
mutation UpdateSpot($id: ID!, $input: UpdateSpot!) {
  updateSpot(id: $id, input: $input) {
    id
    title
  }
}

# Delete a spot
mutation DeleteSpot($id: ID!) {
  deleteSpot(id: $id)
}

Enums

enum MissionType {
  SURVEY
  MODE
  WALK
  VALIDATION
  RATING
  EXTERNAL
}

enum MissionStatus {
  OPEN
  ACTIVE
  CLOSED
  COMPLETED
}

enum MissionProvider {
  GREENGAGE
  EXTERNAL
}

enum PointOfInterestType {
  DEFAULT
  STATION
}

enum DatasetType {
  TEXT
  IMAGE
  NUMBER
  BOOLEAN
}

GeoJSON Support

The API supports GeoJSON for geographic data:

type GeoJSON {
  type: String!
  features: [GeoJSONFeature!]
}

type GeoJSONFeature {
  type: String!
  geometry: GeoJSONGeometry!
  properties: Tile
}

type GeoPosition {
  latitude: Float
  longitude: Float
}

Example: Complete Workflow

1. Login and Get User Info

mutation {
  login(email: "user@example.com", password: "password123") {
    token
  }
}

2. Get Available Observatories

query {
  cities {
    id
    name
    joinable
  }
}

3. Join an Observatory

mutation {
  joinObservatory(id: "vienna-observatory") {
    id
    name
  }
}

4. Get POIs and Missions

query {
  pois(city_id: "vienna-observatory") {
    id
    title
    missions {
      id
      title
      type
    }
  }
}

5. Start and Complete a Mission

mutation {
  startMission(id: "mission-123") {
    id
    status
  }
}

# After completing the task
mutation {
  addMissionDataset(
    mission_id: "mission-123"
    data: { values: [{ type: TEXT, value: "Observation notes" }] }
  ) {
    id
  }
}

Error Handling

GraphQL errors are returned in the standard format:

{
  "errors": [
    {
      "message": "Unauthorized",
      "extensions": {
        "code": "UNAUTHENTICATED"
      }
    }
  ]
}

Common error codes:

Code Description
UNAUTHENTICATED Token missing or invalid
FORBIDDEN User lacks permission
NOT_FOUND Resource not found
BAD_USER_INPUT Invalid input data

Rate Limiting

The API implements rate limiting to ensure fair usage. Current limits:

  • Authenticated requests: 1000 requests/minute
  • Unauthenticated requests: 100 requests/minute

Developer

The GREENGAGE API and Console are developed and maintained by:

Company Sushi Dev GmbH
Headquarters Wiedner Gürtel 28/6, 1040 Wien, Austria
Branch Office Bischofpl. 1/6b, 8010 Graz, Austria
Email office@sushi.dev
Phone +43 1 934 6339

Webhooks

The GREENGAGE API supports webhooks for real-time event notifications.

!!! note "Current Implementation" The current webhooks implementation was originally developed for MindEarth/MindView integration. For general integrations, we recommend using the GraphQL API directly.

Example Implementation

A basic webhook implementation example is available on GitHub:

github.com/sushidev-team/greengage-webhook-example

Webhook Events

Webhooks can be triggered for various events including:

  • Mission completion
  • Spot creation
  • Survey submission
  • User actions

For custom webhook integrations, please contact the development team.


GraphQL Playground

Interactive Explorer

Explore and test the API interactively using the Control Panel:

https://api.greengage.dev/cp

The Control Panel provides:

  • Interactive query builder
  • Full schema documentation
  • Live query execution
  • Response visualization
  • Authentication testing

Apollo Sandbox

The standard Apollo Sandbox is also available at:

https://api.greengage.dev/graphql

Features:

  • Schema introspection
  • Query history
  • Variable editor
  • Response formatting