Skip to main content

Overview

The SIAT system provides two separate environments for integrating your invoicing application: Development (Pilot) and Production. Understanding the differences between these environments is crucial for proper testing and deployment.

Environment Types

Development Environment (Pilot)

The pilot environment is designed for testing and development:
const DevBaseURL = "https://pilotosiatservicios.impuestos.gob.bo/v2"

s, err := siat.New(DevBaseURL, nil)
Characteristics:
  • Safe for testing without affecting real tax data
  • Separate API tokens from production
  • May have relaxed validation rules
  • Data does not affect official tax records
  • Suitable for integration testing and development

Production Environment

The production environment handles real invoicing operations:
const ProdBaseURL = "https://siat.impuestos.gob.bo/v2"

s, err := siat.New(ProdBaseURL, nil)
Characteristics:
  • Handles real tax invoices and declarations
  • Strict validation and security requirements
  • Data is legally binding and reported to tax authorities
  • Requires production API credentials
  • Must be used for actual business operations
Never use production credentials or endpoints during development and testing. Always test thoroughly in the pilot environment first.

Environment Configuration

Basic Environment Setup

The SDK requires two key components for environment configuration:
  1. Base URL: The SIAT service endpoint
  2. API Token: Authentication credentials for the environment
import (
    "github.com/ron86i/go-siat"
    "github.com/ron86i/go-siat/pkg/config"
)

// Development environment
devClient, err := siat.New(
    "https://pilotosiatservicios.impuestos.gob.bo/v2",
    nil,
)

devConfig := config.Config{
    Token: "YOUR_DEV_TOKEN",
}

// Production environment
prodClient, err := siat.New(
    "https://siat.impuestos.gob.bo/v2",
    nil,
)

prodConfig := config.Config{
    Token: "YOUR_PROD_TOKEN",
}

Environment-Aware Configuration

Create a configuration structure that handles both environments:
package main

import (
    "fmt"
    "os"
    
    "github.com/ron86i/go-siat"
    "github.com/ron86i/go-siat/pkg/config"
)

type Environment string

const (
    Development Environment = "development"
    Production  Environment = "production"
)

type SiatConfig struct {
    BaseURL string
    Token   string
}

func GetSiatConfig(env Environment) (*SiatConfig, error) {
    switch env {
    case Development:
        return &SiatConfig{
            BaseURL: "https://pilotosiatservicios.impuestos.gob.bo/v2",
            Token:   os.Getenv("SIAT_DEV_TOKEN"),
        }, nil
    case Production:
        return &SiatConfig{
            BaseURL: "https://siat.impuestos.gob.bo/v2",
            Token:   os.Getenv("SIAT_PROD_TOKEN"),
        }, nil
    default:
        return nil, fmt.Errorf("unknown environment: %s", env)
    }
}

func main() {
    // Get environment from environment variable or command line
    env := Environment(os.Getenv("ENVIRONMENT"))
    if env == "" {
        env = Development // Default to development
    }
    
    cfg, err := GetSiatConfig(env)
    if err != nil {
        panic(err)
    }
    
    // Initialize SDK with environment configuration
    s, err := siat.New(cfg.BaseURL, nil)
    if err != nil {
        panic(err)
    }
    
    authConfig := config.Config{
        Token: cfg.Token,
    }
    
    // Use the configured client
    _ = s
    _ = authConfig
}

Service Endpoints by Environment

Each environment has four main service endpoints. The SDK automatically constructs the full URL by combining the base URL with the service path (as defined in index_service.go:14):
ServicePathFull Development URLFull Production URL
Codigos/FacturacionCodigoshttps://pilotosiatservicios.impuestos.gob.bo/v2/FacturacionCodigoshttps://siat.impuestos.gob.bo/v2/FacturacionCodigos
Operaciones/FacturacionOperacioneshttps://pilotosiatservicios.impuestos.gob.bo/v2/FacturacionOperacioneshttps://siat.impuestos.gob.bo/v2/FacturacionOperaciones
Sincronizacion/FacturacionSincronizacionhttps://pilotosiatservicios.impuestos.gob.bo/v2/FacturacionSincronizacionhttps://siat.impuestos.gob.bo/v2/FacturacionSincronizacion
CompraVenta/ServicioFacturacionCompraVentahttps://pilotosiatservicios.impuestos.gob.bo/v2/ServicioFacturacionCompraVentahttps://siat.impuestos.gob.bo/v2/ServicioFacturacionCompraVenta
The SDK handles URL construction automatically. You only need to provide the base URL when initializing with siat.New().

Environment-Specific Parameters

Certain request parameters have different values depending on the environment:

Codigo Ambiente (Environment Code)

The CodigoAmbiente parameter identifies which environment you’re using:
  • Development: 2 (Pilot/Test environment)
  • Production: 1 (Production environment)
// Development request
devReq := models.Codigos.NewCuisRequest().
    WithCodigoAmbiente(2). // Development
    WithNit(123456789).
    WithCodigoSistema("ABC123").
    Build()

// Production request
prodReq := models.Codigos.NewCuisRequest().
    WithCodigoAmbiente(1). // Production
    WithNit(123456789).
    WithCodigoSistema("ABC123").
    Build()
Always ensure the CodigoAmbiente parameter matches the base URL you’re using. Using mismatched values will result in errors.

Complete Environment Configuration

type RequestConfig struct {
    Environment     Environment
    CodigoAmbiente  int
    Nit             int64
    CodigoSistema   string
    CodigoSucursal  int
    CodigoPuntoVenta int
}

func NewRequestConfig(env Environment, nit int64, sistema string) RequestConfig {
    cfg := RequestConfig{
        Environment:    env,
        Nit:           nit,
        CodigoSistema: sistema,
    }
    
    // Set environment code based on environment
    if env == Production {
        cfg.CodigoAmbiente = 1
    } else {
        cfg.CodigoAmbiente = 2
    }
    
    return cfg
}

Environment Best Practices

1. Use Environment Variables

Store environment-specific configuration in environment variables:
# Development .env file
ENVIRONMENT=development
SIAT_BASE_URL=https://pilotosiatservicios.impuestos.gob.bo/v2
SIAT_API_TOKEN=your_dev_token_here
CODIGO_AMBIENTE=2

# Production .env file
ENVIRONMENT=production
SIAT_BASE_URL=https://siat.impuestos.gob.bo/v2
SIAT_API_TOKEN=your_prod_token_here
CODIGO_AMBIENTE=1
Load them in your application:
import (
    "os"
    "strconv"
    
    "github.com/joho/godotenv" // Popular .env loader
)

func init() {
    // Load .env file
    godotenv.Load()
}

func main() {
    baseURL := os.Getenv("SIAT_BASE_URL")
    token := os.Getenv("SIAT_API_TOKEN")
    codigoAmbiente, _ := strconv.Atoi(os.Getenv("CODIGO_AMBIENTE"))
    
    s, err := siat.New(baseURL, nil)
    if err != nil {
        panic(err)
    }
    
    cfg := config.Config{Token: token}
    
    // Use codigoAmbiente in requests...
}

2. Implement Environment Guards

Prevent accidental production operations:
type SafeSiatClient struct {
    client      *siat.siatServices
    environment Environment
    config      config.Config
}

func (sc *SafeSiatClient) RequireProduction() error {
    if sc.environment != Production {
        return fmt.Errorf("this operation requires production environment")
    }
    return nil
}

func (sc *SafeSiatClient) RequireDevelopment() error {
    if sc.environment != Development {
        return fmt.Errorf("this operation is only allowed in development")
    }
    return nil
}

// Example usage
func (sc *SafeSiatClient) SubmitRealInvoice(ctx context.Context, req any) error {
    if err := sc.RequireProduction(); err != nil {
        return err
    }
    
    // Submit invoice to production
    _, err := sc.client.CompraVenta.RecepcionFactura(ctx, sc.config, req)
    return err
}

3. Separate HTTP Clients per Environment

Use different HTTP client configurations for each environment:
import (
    "net/http"
    "time"
)

func createHTTPClient(env Environment) *http.Client {
    client := &http.Client{}
    
    if env == Development {
        // More lenient timeouts for development
        client.Timeout = 30 * time.Second
    } else {
        // Stricter timeouts for production
        client.Timeout = 15 * time.Second
    }
    
    return client
}

func initializeSiatClient(env Environment) (*siat.siatServices, error) {
    cfg, err := GetSiatConfig(env)
    if err != nil {
        return nil, err
    }
    
    httpClient := createHTTPClient(env)
    return siat.New(cfg.BaseURL, httpClient)
}

4. Environment-Specific Logging

import "log"

type Logger struct {
    env Environment
}

func (l *Logger) Info(msg string, args ...interface{}) {
    prefix := fmt.Sprintf("[%s] ", l.env)
    log.Printf(prefix+msg, args...)
}

func (l *Logger) Warn(msg string, args ...interface{}) {
    if l.env == Production {
        // Send to monitoring service in production
        // alerting.SendWarning(fmt.Sprintf(msg, args...))
    }
    prefix := fmt.Sprintf("[%s] WARNING: ", l.env)
    log.Printf(prefix+msg, args...)
}

Testing Across Environments

Unit Tests (No External Calls)

func TestEnvironmentConfiguration(t *testing.T) {
    tests := []struct {
        name        string
        env         Environment
        expectedURL string
        expectedCode int
    }{
        {
            name:        "Development",
            env:         Development,
            expectedURL: "https://pilotosiatservicios.impuestos.gob.bo/v2",
            expectedCode: 2,
        },
        {
            name:        "Production",
            env:         Production,
            expectedURL: "https://siat.impuestos.gob.bo/v2",
            expectedCode: 1,
        },
    }
    
    for _, tt := range tests {
        t.Run(tt.name, func(t *testing.T) {
            cfg, err := GetSiatConfig(tt.env)
            if err != nil {
                t.Fatalf("Failed to get config: %v", err)
            }
            
            if cfg.BaseURL != tt.expectedURL {
                t.Errorf("Expected URL %s, got %s", tt.expectedURL, cfg.BaseURL)
            }
        })
    }
}

Integration Tests (Development Only)

func TestCuisRequestDevelopment(t *testing.T) {
    if testing.Short() {
        t.Skip("Skipping integration test")
    }
    
    // Force development environment
    cfg, err := GetSiatConfig(Development)
    if err != nil {
        t.Fatalf("Failed to get config: %v", err)
    }
    
    s, err := siat.New(cfg.BaseURL, nil)
    if err != nil {
        t.Fatalf("Failed to create client: %v", err)
    }
    
    authCfg := config.Config{Token: cfg.Token}
    
    req := models.Codigos.NewCuisRequest().
        WithCodigoAmbiente(2). // Development
        WithNit(123456789).
        WithCodigoSistema("TEST123").
        Build()
    
    ctx := context.Background()
    resp, err := s.Codigos.SolicitudCuis(ctx, authCfg, req)
    
    if err != nil {
        t.Fatalf("Request failed: %v", err)
    }
    
    if resp.Body.Content.RespuestaCuis.Codigo == "" {
        t.Error("Expected non-empty CUIS code")
    }
}

Migration from Development to Production

Pre-Production Checklist

1

Complete Development Testing

  • All functionality tested in development environment
  • Edge cases and error scenarios validated
  • Integration tests passing consistently
2

Obtain Production Credentials

  • Register with SIAT for production access
  • Obtain production API token
  • Configure production certificate (if required)
3

Update Environment Configuration

  • Set production base URL
  • Configure production API token
  • Update CodigoAmbiente to 1
  • Verify all environment-specific parameters
4

Implement Monitoring

  • Set up production logging
  • Configure error alerting
  • Implement performance monitoring
  • Set up audit trails for compliance
5

Production Validation

  • Verify communication with production endpoints
  • Test with minimal real transactions
  • Confirm data appears correctly in SIAT system
  • Validate invoice generation and acceptance

Configuration Migration Example

type MigrationConfig struct {
    FromEnv Environment
    ToEnv   Environment
}

func ValidateMigration(mc MigrationConfig) error {
    if mc.FromEnv != Development {
        return fmt.Errorf("can only migrate from development")
    }
    if mc.ToEnv != Production {
        return fmt.Errorf("can only migrate to production")
    }
    
    // Validate production credentials exist
    prodCfg, err := GetSiatConfig(Production)
    if err != nil {
        return fmt.Errorf("production config not available: %w", err)
    }
    
    if prodCfg.Token == "" {
        return fmt.Errorf("production token not configured")
    }
    
    return nil
}

Troubleshooting Environment Issues

Common Problems

Symptom: Requests fail with validation errorsSolution: Ensure CodigoAmbiente matches your base URL:
  • Development URL → CodigoAmbiente = 2
  • Production URL → CodigoAmbiente = 1
Symptom: Authentication errorsSolution: Verify you’re not using development token with production URL or vice versa
Symptom: TLS/SSL handshake failuresSolution: Ensure your Go application trusts the SIAT certificate chain. Update system certificates if needed.
Symptom: Requests timing out in productionSolution: Production may have different response times. Adjust HTTP client timeout settings accordingly.

Environment Comparison

AspectDevelopmentProduction
Base URLpilotosiatservicios.impuestos.gob.bosiat.impuestos.gob.bo
Codigo Ambiente21
Data ImpactTest data onlyLegal tax records
CredentialsSeparate test tokenProduction token
ValidationMay be relaxedStrict compliance
MonitoringOptionalRequired
Error ToleranceHighLow
Rate LimitsUsually higherMay be stricter

Next Steps

Quick Start

Get started with your first API call

API Reference

Explore available API methods

Build docs developers (and LLMs) love