Home Docs Godot

Godot Quick Start

Integrate Indieop API into your Godot games using GDScript.

Prerequisites

Before you begin, make sure you have:

  1. Created an Indieop account
  2. Created a game and obtained your API key from the game settings
  3. Godot 3.5+ or Godot 4.x installed

Overview

This guide shows you how to send player feedback and form submissions from your Godot game to the Indieop API using GDScript. The implementation uses Godot's built-in HTTPRequest node.

Implementation

Step 1: Create the API Client Script

Create a new script called indieop_client.gd:

extends Node
class_name IndieOpClient

const BASE_URL = "https://indieop.com/api/sdk"

var api_key: String
var http_request: HTTPRequest

signal submission_completed(success: bool, message: String)

func _init(p_api_key: String):
    api_key = p_api_key

func _ready():
    # Create HTTPRequest node
    http_request = HTTPRequest.new()
    add_child(http_request)
    http_request.request_completed.connect(_on_request_completed)

func submit_form(submission_data: Dictionary) -> void:
    var url = BASE_URL + "/submit"
    var headers = [
        "Content-Type: application/json",
        "X-API-Key: " + api_key
    ]
    
    var json_data = JSON.stringify(submission_data)
    var error = http_request.request(url, headers, HTTPClient.METHOD_POST, json_data)
    
    if error != OK:
        push_error("Failed to send HTTP request: " + str(error))
        submission_completed.emit(false, "Failed to send request")

func _on_request_completed(result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray):
    var json = JSON.new()
    var parse_result = json.parse(body.get_string_from_utf8())
    
    if parse_result != OK:
        push_error("Failed to parse JSON response")
        submission_completed.emit(false, "Failed to parse response")
        return
    
    var response = json.get_data()
    
    if response_code == 200 or response_code == 201:
        print("Submission successful: ", response.message)
        submission_completed.emit(true, response.message)
    else:
        push_error("Submission failed: " + response.message)
        submission_completed.emit(false, response.message)

# Helper function to create a field
static func create_field(key: String, label: String, type: String, value, options = null) -> Dictionary:
    var field = {
        "key": key,
        "label": label,
        "type": type,
        "value": value
    }
    if options != null:
        field["options"] = options
    return field

Godot 3.x Users: The syntax is slightly different in Godot 3. Replace request_completed.connect() with connect("request_completed", ...) and use PoolByteArray instead of PackedByteArray.

Step 2: Create a Feedback UI Scene

Create a new scene with a feedback form. Here's the script for the feedback scene:

extends Control

# Reference to UI elements (assign in the editor)
@onready var rating_slider = $VBoxContainer/RatingSlider
@onready var comments_text = $VBoxContainer/CommentsTextEdit
@onready var difficulty_option = $VBoxContainer/DifficultyOptionButton
@onready var submit_button = $VBoxContainer/SubmitButton
@onready var status_label = $VBoxContainer/StatusLabel

var indieop_client: IndieOpClient

func _ready():
    # Initialize the client
    indieop_client = IndieOpClient.new("your_game_api_key_here")
    add_child(indieop_client)
    
    # Connect signals
    submit_button.pressed.connect(_on_submit_pressed)
    indieop_client.submission_completed.connect(_on_submission_completed)
    
    # Setup difficulty options
    difficulty_option.clear()
    difficulty_option.add_item("Easy")
    difficulty_option.add_item("Medium")
    difficulty_option.add_item("Hard")
    difficulty_option.select(1)  # Default to Medium

func _on_submit_pressed():
    submit_button.disabled = true
    status_label.text = "Submitting..."
    
    var rating = int(rating_slider.value)
    var comments = comments_text.text
    var difficulty = difficulty_option.get_item_text(difficulty_option.selected)
    
    var submission = {
        "form_name": "Player Feedback",
        "tag": "feedback",
        "form_type": "feedback",
        "player_identifier": OS.get_unique_id(),
        "game_version": ProjectSettings.get_setting("application/config/version"),
        "fields": [
            IndieOpClient.create_field("rating", "Overall Rating", "rating", rating),
            IndieOpClient.create_field("comments", "Your Feedback", "text", comments),
            IndieOpClient.create_field("difficulty", "Difficulty Level", "dropdown", difficulty,
                ["Easy", "Medium", "Hard"]),
            IndieOpClient.create_field("enjoyed", "Did you enjoy?", "checkbox", true)
        ]
    }
    
    indieop_client.submit_form(submission)

func _on_submission_completed(success: bool, message: String):
    submit_button.disabled = false
    
    if success:
        status_label.text = "Thank you for your feedback!"
        status_label.add_theme_color_override("font_color", Color.GREEN)
        # Clear form
        comments_text.text = ""
        rating_slider.value = 5
    else:
        status_label.text = "Failed to submit: " + message
        status_label.add_theme_color_override("font_color", Color.RED)

Step 3: Create the UI Layout

In the Godot editor, create this node structure for your feedback scene:

Control (FeedbackScene)
└── VBoxContainer
    ├── Label (title)
    ├── HBoxContainer
    │   ├── Label ("Rating:")
    │   └── HSlider (RatingSlider) - min: 1, max: 5
    ├── Label ("Difficulty:")
    ├── OptionButton (DifficultyOptionButton)
    ├── Label ("Comments:")
    ├── TextEdit (CommentsTextEdit)
    ├── Button (SubmitButton)
    └── Label (StatusLabel)

Bug Report Example

Here's how to create a simple bug report system:

extends Node

var indieop_client: IndieOpClient

func _ready():
    indieop_client = IndieOpClient.new("your_game_api_key_here")
    add_child(indieop_client)

func submit_bug_report(description: String, severity: String):
    var submission = {
        "form_name": "Bug Report",
        "tag": "bugs",
        "form_type": "bug_report",
        "player_identifier": OS.get_unique_id(),
        "game_version": ProjectSettings.get_setting("application/config/version"),
        "repository_version": "main@abc123",
        "fields": [
            IndieOpClient.create_field("description", "Bug Description", "text", description),
            IndieOpClient.create_field("severity", "Severity", "dropdown", severity,
                ["Low", "Medium", "High", "Critical"]),
            IndieOpClient.create_field("reproducible", "Can Reproduce?", "checkbox", true)
        ]
    }
    
    indieop_client.submit_form(submission)
    indieop_client.submission_completed.connect(_on_bug_report_submitted)

func _on_bug_report_submitted(success: bool, message: String):
    if success:
        print("Bug report submitted successfully")
    else:
        print("Failed to submit bug report: ", message)

# Example: Call this when player encounters a bug
func _unhandled_input(event):
    if event.is_action_pressed("report_bug"):  # Add this input action in Project Settings
        submit_bug_report("Player fell through floor in Level 3", "High")

Autoload Setup (Recommended)

For easier access throughout your game, set up the IndieOp client as an autoload:

  1. Create a global script called indieop_global.gd:
    extends Node
    
    var client: IndieOpClient
    
    func _ready():
        client = IndieOpClient.new("your_game_api_key_here")
        add_child(client)
    
    func submit_feedback(rating: int, comments: String):
        var submission = {
            "form_name": "Player Feedback",
            "tag": "feedback",
            "form_type": "feedback",
            "player_identifier": OS.get_unique_id(),
            "game_version": ProjectSettings.get_setting("application/config/version"),
            "fields": [
                IndieOpClient.create_field("rating", "Overall Rating", "rating", rating),
                IndieOpClient.create_field("comments", "Comments", "text", comments)
            ]
        }
        client.submit_form(submission)
  2. Add to autoload: Go to Project → Project Settings → Autoload, and add indieop_global.gd with the name "IndieOp"
  3. Use anywhere in your game:
    # Can now access from any script
    IndieOp.submit_feedback(5, "Amazing game!")

Field Types Reference

The Indieop API supports four field types:

Field Type GDScript Type Example
text String "Great game!"
rating int (1-5) 5
checkbox bool true
dropdown String (from Array) "Medium"

Error Handling

Implement retry logic and offline queueing:

extends Node

var failed_submissions: Array = []
var retry_timer: Timer

func _ready():
    # Setup retry timer
    retry_timer = Timer.new()
    add_child(retry_timer)
    retry_timer.wait_time = 60.0  # Retry every minute
    retry_timer.timeout.connect(_retry_failed_submissions)
    retry_timer.start()

func store_failed_submission(submission_data: Dictionary):
    failed_submissions.append({
        "data": submission_data,
        "timestamp": Time.get_unix_time_from_system()
    })
    print("Submission stored for retry. Queue size: ", failed_submissions.size())

func _retry_failed_submissions():
    if failed_submissions.is_empty():
        return
    
    print("Retrying ", failed_submissions.size(), " failed submissions...")
    var remaining = []
    
    for item in failed_submissions:
        # Try to resubmit
        var client = get_node("IndieOpClient")  # Or use autoload
        client.submit_form(item.data)
        
        # Check if successful via signal
        # If not, keep in queue
        remaining.append(item)
    
    failed_submissions = remaining

Best Practices

  • Use autoload for global access: Makes the client available throughout your game
  • Store API key in environment: Use Godot's feature tags or external config files
  • Handle offline scenarios: Queue submissions when network is unavailable
  • Validate before sending: Check field values match expected types
  • Rate limiting: Don't exceed 100 requests per minute
  • Free HTTPRequest nodes: Call queue_free() when done if creating many

Next Steps