Skip to main content

Overview

The rating system in IOTA Repstation allows parties to rate each other after completing a deal. Ratings are permanent, blockchain-recorded assessments that build into reputation profiles over time.

Rating Requirements

Deal-Bound Ratings

All ratings must be tied to a specific deal:
  • Deal must be in CLOSED state
  • Only deal participants can rate each other
  • Each participant can rate the other party once per deal
  • Ratings reference the specific deal and subject

Score Range

Ratings use a standardized 1-100 score system:
  • 1-20: Very Poor
  • 21-40: Poor
  • 41-60: Average
  • 61-80: Good
  • 81-100: Excellent

Rating Categories

Categories help organize different aspects of performance:

Common Categories

  • reliability - Did they deliver as promised?
  • communication - How well did they communicate?
  • quality - Was the work/product high quality?
  • speed - How quickly did they complete the work?
  • professionalism - Were they professional throughout?

Custom Categories

Applications can define their own categories:
// NFT Marketplace
const categories = [
  'artwork_quality',
  'authenticity',
  'shipping_speed',
  'packaging'
];

// Service Platform  
const categories = [
  'technical_skill',
  'deadline_adherence', 
  'code_quality',
  'project_management'
];

Creating Ratings

Basic Rating

const { endorsement_id } = await client.rate({
  signer: raterWallet,
  deal_id: completedDealId,
  score: 85,
  category: 'reliability'
});

Multiple Category Ratings

Rate different aspects separately:
// Rate communication
await client.rate({
  signer: buyerWallet,
  deal_id: dealId,
  score: 92,
  category: 'communication'
});

// Rate delivery speed
await client.rate({
  signer: buyerWallet,
  deal_id: dealId,
  score: 78,
  category: 'speed'
});

// Rate product quality
await client.rate({
  signer: buyerWallet,
  deal_id: dealId,
  score: 95,
  category: 'quality'
});

Rating Management

Updating Ratings

Ratings can be updated using the endorsement capability:
await client.updateRating({
  signer: raterWallet,
  endorsement_id: endorsementId,
  endorsement_cap_id: endorsementCapId,
  new_score: 90, // Updated score
});

Revoking Ratings

Ratings can be completely revoked:
await client.revokeRating({
  signer: raterWallet,
  endorsement_id: endorsementId,
  endorsement_cap_id: endorsementCapId
});
Revoked ratings are permanently removed and cannot be restored. Use updates instead of revocation when possible.

Rating Aggregation

Global Reputation

All ratings for a user are aggregated globally:
const profile = await client.getReputationProfile({
  walletAddress: userWallet
});

console.log(`Global average: ${profile.global.average}/100`);
console.log(`Total ratings: ${profile.global.count}`);

App-Scoped Reputation

Ratings are also tracked per application:
// Each app namespace maintains separate stats
profile.appScoped.forEach(app => {
  console.log(`App: ${app.appOwner}`);
  console.log(`Average: ${app.stats.average}/100`);
  console.log(`Count: ${app.stats.count}`);
});

Rating Data Structure

Endorsement Object

interface Endorsement {
  id: string;
  rater: string;           // Wallet address of rater
  deal_id: string;         // Associated deal
  score: number;           // 1-100 rating score
  category: string;        // Rating category
  timestamp: string;       // When rating was created
  last_updated?: string;   // When rating was last modified
}

Rating Statistics

interface RatingStats {
  average: number;         // Average of all ratings
  count: number;          // Total number of ratings
  distribution?: {        // Optional score distribution
    excellent: number;    // 81-100 scores
    good: number;        // 61-80 scores  
    average: number;     // 41-60 scores
    poor: number;        // 21-40 scores
    very_poor: number;   // 1-20 scores
  };
}

Best Practices

1. Meaningful Categories

Use categories that matter to your use case:
// Good for marketplaces
const marketplaceCategories = [
  'product_quality',
  'shipping_speed', 
  'customer_service',
  'value_for_money'
];

// Good for freelancing
const freelanceCategories = [
  'skill_level',
  'communication',
  'deadline_compliance',
  'work_quality'
];

2. Encourage Honest Ratings

Design your UI to promote fair ratings:
function RatingComponent({ onSubmit }) {
  const [scores, setScores] = useState({});
  
  return (
    <div className="rating-form">
      <h3>Rate Your Experience</h3>
      
      <RatingSlider
        label="Communication (How well did they communicate?)"
        value={scores.communication || 50}
        onChange={(score) => setScores({...scores, communication: score})}
        helpText="Consider responsiveness, clarity, and professionalism"
      />
      
      <RatingSlider
        label="Quality (How was the work quality?)"  
        value={scores.quality || 50}
        onChange={(score) => setScores({...scores, quality: score})}
        helpText="Consider attention to detail and meeting requirements"
      />
      
      <button onClick={() => submitRatings(scores)}>
        Submit Ratings
      </button>
    </div>
  );
}

3. Rating Timing

Prompt for ratings at the right time:
class OrderService {
  async markOrderDelivered(orderId: string) {
    const order = await this.getOrder(orderId);
    
    // Close the deal first
    await this.reputation.closeDeal({
      signer: this.adminWallet,
      deal_id: order.reputationDealId
    });
    
    // Send rating reminders after short delay
    setTimeout(() => {
      this.sendRatingReminder(order.buyerId, order.id);
      this.sendRatingReminder(order.sellerId, order.id);
    }, 24 * 60 * 60 * 1000); // 24 hours
  }
}

Error Handling

Common rating errors:
try {
  await client.rate({
    signer: wallet,
    deal_id: dealId,
    score: score,
    category: category
  });
} catch (error) {
  switch (error.code) {
    case 'DEAL_NOT_CLOSED':
      showError('Deal must be completed before rating');
      break;
    case 'ALREADY_RATED':
      showError('You have already rated this deal. Use update instead.');
      break;
    case 'NOT_DEAL_PARTICIPANT':
      showError('Only deal participants can rate each other');
      break;
    case 'INVALID_SCORE':
      showError('Rating must be between 1 and 100');
      break;
    case 'CANNOT_RATE_SELF':
      showError('You cannot rate yourself');
      break;
  }
}

Integration Examples

E-commerce Rating Flow

class EcommerceRatings {
  async handleOrderCompletion(orderId: string) {
    const order = await this.db.orders.findById(orderId);
    
    // Close reputation deal
    await this.reputation.closeDeal({
      signer: this.adminWallet,
      deal_id: order.reputationDealId
    });
    
    // Enable rating UI for both parties
    await this.enableRatings(order.buyerId, order.sellerId, order.id);
  }
  
  async submitBuyerRating(buyerId: string, orderId: string, ratings: any) {
    const order = await this.db.orders.findById(orderId);
    
    // Rate seller on multiple categories
    for (const [category, score] of Object.entries(ratings)) {
      await this.reputation.rate({
        signer: buyerId,
        deal_id: order.reputationDealId,
        score: score as number,
        category
      });
    }
  }
}

Service Platform Ratings

class ServiceRatings {
  async completeProject(projectId: string, clientId: string, providerId: string) {
    const project = await this.getProject(projectId);
    
    // Close deal
    await this.reputation.closeDeal({
      signer: clientId,
      deal_id: project.dealId
    });
    
    // Client rates provider
    await this.reputation.rate({
      signer: clientId,
      deal_id: project.dealId,
      score: project.clientRating.skill,
      category: 'technical_skill'
    });
    
    // Provider rates client
    await this.reputation.rate({
      signer: providerId,
      deal_id: project.dealId,
      score: project.providerRating.clarity,
      category: 'requirements_clarity'
    });
  }
}

Next Steps